├── .gitignore ├── CMakeLists.txt ├── README.md ├── Tutorial ├── FFmpeg工具使用 │ ├── ffmpeg视频转码.bat │ ├── ffplay播放视频.bat │ ├── ffprobe文件分析.bat │ └── video │ │ ├── 1_soccor.h264 │ │ ├── 2_football.h264 │ │ ├── IMG_0886.MOV │ │ ├── VideoCameraRecorder.avi │ │ └── walk_qcif.yuv ├── README.md └── SDK_Demo │ ├── AVIOTest │ ├── AVIOTest.cpp │ ├── CMakeLists.txt │ └── bin │ │ └── out.bin │ ├── CMakeLists.txt │ ├── VideoDecoding │ ├── CMakeLists.txt │ ├── Decoder.cpp │ ├── Decoder.h │ ├── InputOutput.cpp │ ├── InputOutput.h │ ├── VideoDecodingHeader.h │ └── VideoDecodingMain.cpp │ ├── VideoDemuxing │ ├── AVDecoder.cpp │ ├── AVDecoder.h │ ├── CMakeLists.txt │ ├── DemuxingContext.cpp │ ├── DemuxingContext.h │ ├── IOFile.h │ ├── VideoDemuxingHeader.h │ └── VideoDemuxingMain.cpp │ ├── VideoEncoding │ ├── CMakeLists.txt │ ├── Encoder.cpp │ ├── Encoder.h │ ├── InputOutput.cpp │ ├── InputOutput.h │ ├── VideoEncodingHeader.h │ └── VideoEncodingMain.cpp │ ├── VideoEncodingMuxing │ ├── CMakeLists.txt │ ├── CoderMuxer.cpp │ ├── CoderMuxer.h │ ├── EncodingMuxingAudio.cpp │ ├── EncodingMuxingAudio.h │ ├── EncodingMuxingHeader.h │ ├── EncodingMuxingMain.cpp │ ├── EncodingMuxingVideo.cpp │ ├── EncodingMuxingVideo.h │ ├── Stream.cpp │ ├── Stream.h │ └── common.h │ ├── VideoFiltering │ ├── CMakeLists.txt │ ├── Filter.cpp │ ├── Filter.h │ ├── Frame.cpp │ ├── Frame.h │ ├── IOFiles.cpp │ ├── IOFiles.h │ ├── VideoFilteringHeader.h │ ├── VideoFilteringMain.cpp │ └── common.h │ ├── VideoRemuxing │ ├── CMakeLists.txt │ ├── IOFiles.h │ ├── VideoRemuxingHeader.h │ ├── VideoRemuxingMain.cpp │ └── common.h │ └── VideoScaling │ ├── CMakeLists.txt │ ├── IOFiles.h │ ├── VideoScalingHeader.h │ ├── VideoScalingMain.cpp │ └── common.h ├── chapter01 ├── 01-音视频技术概述.md ├── pic1.jpg ├── pic2.jpg ├── pic3.jpg └── pic4.jpg ├── chapter02 ├── 02-图像、像素与颜色空间.md ├── pic1.jpg ├── pic2.jpg ├── pic3.jpg └── pic4.jpg ├── chapter03 ├── 03-视频压缩编码.md ├── pic1.jpg └── pic2.jpg ├── chapter04 └── 04-音频压缩编码.md ├── chapter05 └── 05-音视频文件容器和封装格式.md ├── chapter06 └── 06-音视频流媒体协议.md ├── chapter07 └── 07-FFmpeg的基本操作.md ├── chapter08 └── 08-滤镜图.md ├── chapter09 └── 09-流媒体应用.md ├── chapter10 ├── 10-FFmpeg SDK的使用.md ├── CMakeLists.txt └── cmd_dir.cpp ├── chapter11 ├── 11-使用FFmpeg SDK进行视频编解码.md ├── CMakeLists.txt ├── video_decoder.cpp └── video_encoder.cpp ├── chapter12 ├── 12-使用FFmpeg SDK进行音频编解码.md ├── CMakeLists.txt ├── audio_decoder.cpp └── audio_encoder.cpp ├── chapter13 ├── 13-使用FFmpeg SDK进行音视频文件的解封装与封装.md ├── CMakeLists.txt ├── demuxer.cpp └── muxer.cpp ├── chapter14 ├── 14-使用FFmpeg SDK添加视频滤镜和音频滤镜.md ├── CMakeLists.txt ├── audio_filter.cpp └── video_filter.cpp ├── chapter15 ├── 15-使用FFmpeg SDK进行视频图像转换与音频重采样.md ├── CMakeLists.txt ├── audio_resampler.cpp └── video_transformer.cpp ├── inc ├── audio_decoder_core.h ├── audio_encoder_core.h ├── audio_filter_core.h ├── audio_resampler_core.h ├── demuxer_core.h ├── io_data.h ├── muxer_core.h ├── video_decoder_core.h ├── video_encoder_core.h ├── video_filter_core.h └── video_swscale_core.h ├── main.cpp ├── src ├── audio_decoder_core.cpp ├── audio_encoder_core.cpp ├── audio_filter_core.cpp ├── audio_resampler_core.cpp ├── demuxer_core.cpp ├── io_data.cpp ├── muxer_core.cpp ├── video_decoder_core.cpp ├── video_encoder_core.cpp ├── video_filter_core.cpp └── video_swscale_core.cpp └── video ├── slamtv10.264 └── test.264 /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build-debug 2 | cmake-build-release 3 | .idea -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | project(FFmpeg_Demo) 3 | 4 | include_directories(inc) 5 | 6 | set(SRC_DIRS src) 7 | foreach (src_dir ${SRC_DIRS}) 8 | aux_source_directory(${src_dir} src_dir_file) 9 | set(SRC_FILES ${SRC_FILES} ${src_dir_file}) 10 | endforeach (src_dir) 11 | 12 | 13 | set(LINK_LIBS m pthread jpeg iconv sqlite3 ssl crypto x264) 14 | set(FFMPEG_LIBS avcodec avformat avutil swresample swscale avfilter) 15 | 16 | set(CMAKE_CXX_STANDARD 11) 17 | 18 | add_executable(FFmpeg_Demo main.cpp) 19 | 20 | add_subdirectory(chapter10) 21 | add_subdirectory(chapter11) 22 | add_subdirectory(chapter12) 23 | add_subdirectory(chapter13) 24 | add_subdirectory(chapter14) 25 | add_subdirectory(chapter15) 26 | 27 | add_subdirectory(Tutorial/SDK_Demo) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FFmpeg音视频开发基础与实战 2 | 编码格式转换网站 http://www.mytju.com/classcode/tools/encode_utf8.asp 3 | ## [01-音视频技术概述](chapter01/01-音视频技术概述.md) 4 | ## [02-图像、像素与颜色空间](chapter02/02-图像、像素与颜色空间.md) 5 | ## [03-视频压缩编码](chapter03/03-视频压缩编码.md) 6 | ## [04-音频压缩编码](chapter04/04-音频压缩编码.md) 7 | ## [05-音视频文件容器和封装格式](chapter05/05-音视频文件容器和封装格式.md) 8 | ## [06-音视频流媒体协议](chapter06/06-音视频流媒体协议.md) 9 | ## [07-FFmpeg的基本操作](chapter07/07-FFmpeg的基本操作.md) 10 | ## [08-滤镜图](chapter08/08-滤镜图.md) 11 | ## [09-流媒体应用](chapter09/09-流媒体应用.md) 12 | ## [10-FFmpeg SDK的使用](chapter10/10-FFmpeg SDK的使用.md) 13 | ## [11-使用FFmpeg SDK进行视频编解码](chapter11/11-使用FFmpeg SDK进行视频编解码.md) 14 | ## [12-使用FFmpeg SDK进行音频编解码](chapter12/12-使用FFmpeg SDK进行音频编解码.md) 15 | ## [13-使用FFmpeg SDK进行音视频文件的解封装与封装](chapter13/13-使用FFmpeg SDK进行音视频文件的解封装与封装.md) 16 | ## [14-使用FFmpeg SDK添加视频滤镜和音频滤镜](chapter14/14-使用FFmpeg SDK添加视频滤镜和音频滤镜.md) 17 | ## [15-使用FFmpeg SDK进行视频图像转换与音频重采样](chapter15/15-使用FFmpeg SDK进行视频图像转换与音频重采样.md) 18 | 19 | ## [Tutorial-FFmpeg SDK Tutorial](Tutorial/README.md) -------------------------------------------------------------------------------- /Tutorial/FFmpeg工具使用/ffmpeg视频转码.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/Tutorial/FFmpeg工具使用/ffmpeg视频转码.bat -------------------------------------------------------------------------------- /Tutorial/FFmpeg工具使用/ffplay播放视频.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | TITLE FFPlay 4 | 5 | IF NOT EXIST bin\ffplay.exe ( 6 | CLS 7 | ECHO bin\ffplay.exe could not be found. 8 | GOTO:error 9 | ) 10 | 11 | ECHO ffplay found. 12 | CD bin || GOTO:error 13 | 14 | ffplay.exe -window_title "FFPlay Demo" -x 720 -y 576 -t 00:10 -autoexit -i ../video/out.h264 15 | 16 | :error 17 | ECHO. 18 | ECHO Press any key to exit. 19 | PAUSE >nul 20 | GOTO:EOF -------------------------------------------------------------------------------- /Tutorial/FFmpeg工具使用/ffprobe文件分析.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | TITLE FFprobe 4 | 5 | IF NOT EXIST bin\ffprobe.exe ( 6 | CLS 7 | ECHO bin\ffprobe.exe could not be found. 8 | GOTO:error 9 | ) 10 | 11 | ECHO ffprobe found. 12 | CD bin || GOTO:error 13 | 14 | ffprobe.exe -i ../video/VideoCameraRecorder.avi 15 | 16 | :error 17 | ECHO. 18 | ECHO Press any key to exit. 19 | PAUSE >nul 20 | GOTO:EOF -------------------------------------------------------------------------------- /Tutorial/FFmpeg工具使用/video/1_soccor.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/Tutorial/FFmpeg工具使用/video/1_soccor.h264 -------------------------------------------------------------------------------- /Tutorial/FFmpeg工具使用/video/2_football.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/Tutorial/FFmpeg工具使用/video/2_football.h264 -------------------------------------------------------------------------------- /Tutorial/FFmpeg工具使用/video/IMG_0886.MOV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/Tutorial/FFmpeg工具使用/video/IMG_0886.MOV -------------------------------------------------------------------------------- /Tutorial/FFmpeg工具使用/video/VideoCameraRecorder.avi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/Tutorial/FFmpeg工具使用/video/VideoCameraRecorder.avi -------------------------------------------------------------------------------- /Tutorial/FFmpeg工具使用/video/walk_qcif.yuv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/Tutorial/FFmpeg工具使用/video/walk_qcif.yuv -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/AVIOTest/AVIOTest.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | extern "C" 6 | { 7 | #include 8 | #include 9 | #include 10 | } 11 | 12 | typedef uint8_t BYTE; 13 | 14 | FILE *input_file = nullptr; 15 | BYTE *file_buffer = nullptr, *avio_ctx_buffer = nullptr; 16 | size_t file_buffer_size = 0, avio_ctx_buffer_size = 16; 17 | 18 | typedef struct buffer_data 19 | { 20 | BYTE *ptr; 21 | size_t size; ///< size left in the buffer 22 | } BufferData; 23 | 24 | BufferData block_data = { 0 }; 25 | 26 | static int read_packet(void *opaque, uint8_t *buf, int buf_size) 27 | { 28 | struct buffer_data *bd = (struct buffer_data *)opaque; 29 | buf_size = FFMIN(buf_size, bd->size); 30 | 31 | printf("ptr:%p size:%zu\n", bd->ptr, bd->size); 32 | 33 | /* copy internal buffer data to buf */ 34 | memcpy(buf, bd->ptr, buf_size); 35 | bd->ptr += buf_size; 36 | bd->size -= buf_size; 37 | 38 | return buf_size; 39 | } 40 | 41 | int main(int argc, char** argv) 42 | { 43 | AVIOContext *avio_ctx = nullptr; 44 | 45 | if (argc < 2) 46 | { 47 | printf("Error: input file name missing...\n"); 48 | return -1; 49 | } 50 | 51 | input_file = fopen(argv[1], "rb"); 52 | if (!input_file) 53 | { 54 | printf("Error: opening input file failed.\n"); 55 | return -1; 56 | } 57 | 58 | fseek(input_file, 0, SEEK_END); 59 | file_buffer_size = ftell(input_file); 60 | fseek(input_file, 0, SEEK_SET); 61 | file_buffer = (BYTE *)av_mallocz(file_buffer_size); 62 | fread(file_buffer, 1, file_buffer_size, input_file); 63 | 64 | BufferData bd = { file_buffer, file_buffer_size }; 65 | 66 | 67 | avio_ctx_buffer = (BYTE *)av_malloc(avio_ctx_buffer_size); 68 | avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size, 0, &bd, read_packet, NULL, NULL); 69 | if (!avio_ctx) 70 | { 71 | printf("Error: allocating AVIOContext failed.\n"); 72 | goto end; 73 | } 74 | 75 | printf("Output in main 1:\n"); 76 | for (int i = 0; i < 8; i++) { 77 | printf("0x%x\t", avio_r8(avio_ctx)); 78 | if ((i+1) % 8 == 0) { 79 | printf("\n"); 80 | } 81 | } 82 | 83 | end: 84 | if (avio_ctx) { 85 | av_freep(&avio_ctx->buffer); 86 | av_freep(&avio_ctx); 87 | } 88 | fclose(input_file); 89 | input_file = nullptr; 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/AVIOTest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(AVIOTest AVIOTest.cpp) 2 | target_link_libraries(AVIOTest ${LINK_LIBS} ${FFMPEG_LIBS}) -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/AVIOTest/bin/out.bin: -------------------------------------------------------------------------------- 1 |  2 |  !"#$%&'()*+,-./0123456789:;<=>? -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(AVIOTest) 2 | add_subdirectory(VideoDecoding) 3 | add_subdirectory(VideoEncoding) 4 | add_subdirectory(VideoEncodingMuxing) 5 | add_subdirectory(VideoRemuxing) 6 | add_subdirectory(VideoDemuxing) 7 | add_subdirectory(VideoFiltering) 8 | add_subdirectory(VideoScaling) 9 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDecoding/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(.) 2 | add_executable(VideoDecodingMain VideoDecodingMain.cpp Decoder.cpp InputOutput.cpp) 3 | target_link_libraries(VideoDecodingMain ${LINK_LIBS} ${FFMPEG_LIBS}) -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDecoding/Decoder.cpp: -------------------------------------------------------------------------------- 1 | #include "Decoder.h" 2 | #include "VideoDecodingHeader.h" 3 | 4 | bool Open_deocder(CodecCtx &ctx) { 5 | //注册编解码器对象 6 | avcodec_register_all(); 7 | 8 | //初始化AVPacket对象 9 | av_init_packet(&(ctx.pkt)); 10 | 11 | //根据CODEC_ID查找AVCodec对象 12 | ctx.pCodec = avcodec_find_decoder(AV_CODEC_ID_H264); 13 | if (!ctx.pCodec) { 14 | fprintf(stderr, "Codec not found\n"); 15 | return false; 16 | } 17 | 18 | //根据AVCodec对象分配AVCodecContext 19 | ctx.pCodecContext = avcodec_alloc_context3(ctx.pCodec); 20 | if (!ctx.pCodecContext) { 21 | fprintf(stderr, "Could not allocate video codec context\n"); 22 | return false; 23 | } 24 | 25 | if (ctx.pCodec->capabilities & AV_CODEC_CAP_TRUNCATED) 26 | ctx.pCodecContext->flags |= AV_CODEC_FLAG_TRUNCATED; // we do not send complete frames 27 | 28 | //根据CODEC_ID初始化AVCodecParserContext对象 29 | ctx.pCodecParserCtx = av_parser_init(AV_CODEC_ID_H264); 30 | if (!ctx.pCodecParserCtx) { 31 | printf("Could not allocate video parser context\n"); 32 | return false; 33 | } 34 | 35 | //打开AVCodec对象 36 | if (avcodec_open2(ctx.pCodecContext, ctx.pCodec, NULL) < 0) { 37 | fprintf(stderr, "Could not open codec\n"); 38 | return false; 39 | } 40 | 41 | //分配AVFrame对象 42 | ctx.frame = av_frame_alloc(); 43 | if (!ctx.frame) { 44 | fprintf(stderr, "Could not allocate video frame\n"); 45 | return false; 46 | } 47 | 48 | return true; 49 | } 50 | 51 | void Close_decoder(CodecCtx &ctx) { 52 | avcodec_close(ctx.pCodecContext); 53 | av_free(ctx.pCodecContext); 54 | av_frame_free(&(ctx.frame)); 55 | } 56 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDecoding/Decoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef _DECODER_H_ 3 | #define _DECODER_H_ 4 | 5 | #include "VideoDecodingHeader.h" 6 | 7 | /************************************************* 8 | Function: Open_deocder 9 | Description: 打开输入输出文件 10 | Calls: 无 11 | Called By: main 12 | Input: (in/out)ctx : 注册编解码器结构并打开AVCodec、AVCodecContext、AVCodecParserContext等结构 13 | Output: (in/out)ctx : 用结构体成员保存打开文件的指针 14 | Return: true : 打开输入输出文件正确 15 | false : 打开输入输出文件错误 16 | *************************************************/ 17 | bool Open_deocder(CodecCtx &ctx); 18 | 19 | /************************************************* 20 | Function: Close_decoder 21 | Description: 打开输入输出文件 22 | Calls: 无 23 | Called By: main 24 | Input: (in/out)ctx : 解码器结构 25 | Output: (in/out)ctx : 解码器结构 26 | Return: 无 27 | *************************************************/ 28 | void Close_decoder(CodecCtx &ctx); 29 | 30 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDecoding/InputOutput.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "InputOutput.h" 4 | 5 | bool Open_files(IOParam &IOParam) { 6 | IOParam.pFin = fopen(IOParam.pNameIn, "rb"); 7 | if (!IOParam.pFin) { 8 | printf("Could not open %s\n", IOParam.pNameIn); 9 | return false; 10 | } 11 | 12 | IOParam.pFout = fopen(IOParam.pNameOut, "wb"); 13 | if (!IOParam.pFout) { 14 | printf("Could not open %s\n", IOParam.pNameOut); 15 | return false; 16 | } 17 | 18 | return true; 19 | } 20 | 21 | void Parse(int argc, char **argv, IOParam &IOParam) { 22 | IOParam.pNameIn = argv[1]; 23 | IOParam.pNameOut = argv[2]; 24 | } 25 | 26 | void Close_files(IOParam &IOParam) { 27 | fclose(IOParam.pFin); 28 | fclose(IOParam.pFout); 29 | } 30 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDecoding/InputOutput.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef _INPUT_OUTPUT_H_ 3 | #define _INPUT_OUTPUT_H_ 4 | 5 | /************************************************* 6 | Struct: IOParam 7 | Description: 接收命令行参数 8 | *************************************************/ 9 | typedef struct { 10 | FILE *pFin; 11 | FILE *pFout; 12 | 13 | char *pNameIn; 14 | char *pNameOut; 15 | } IOParam; 16 | 17 | /************************************************* 18 | Function: Parse_input_param 19 | Description: 解析命令行传入的参数 20 | Calls: 无 21 | Called By: main 22 | Input: (in)argc : 默认命令行参数 23 | (in)argv : 默认命令行参数 24 | Output: (out)io_param : 解析命令行的结果 25 | Return: true : 命令行解析正确 26 | false : 命令行解析错误 27 | *************************************************/ 28 | void Parse(int argc, char **argv, IOParam &IOParam); 29 | 30 | /************************************************* 31 | Function: Open_file 32 | Description: 打开输入输出文件 33 | Calls: 无 34 | Called By: main 35 | Input: (in/out)io_param : 传入文件的路径 36 | Output: (in/out)io_param : 用结构体成员保存打开文件的指针 37 | Return: true : 打开输入输出文件正确 38 | false : 打开输入输出文件错误 39 | *************************************************/ 40 | bool Open_files(IOParam &IOParam); 41 | 42 | /************************************************* 43 | Function: Close_file 44 | Description: 关闭输入输出文件 45 | Calls: 无 46 | Called By: main 47 | Input: (in)io_param : 保存输入输出文件的指针 48 | Output: 无 49 | Return: 无 50 | *************************************************/ 51 | void Close_files(IOParam &IOParam); 52 | 53 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDecoding/VideoDecodingHeader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef _VIDEO_DECODING_HEADER_ 4 | #define _VIDEO_DECODING_HEADER_ 5 | 6 | #define INBUF_SIZE 4096 7 | #define AUDIO_INBUF_SIZE 20480 8 | #define AUDIO_REFILL_THRESH 4096 9 | 10 | extern "C" 11 | { 12 | #include "libavutil/opt.h" 13 | #include "libavcodec/avcodec.h" 14 | #include "libavutil/channel_layout.h" 15 | #include "libavutil/common.h" 16 | #include "libavutil/imgutils.h" 17 | #include "libavutil/mathematics.h" 18 | #include "libavutil/samplefmt.h" 19 | } 20 | 21 | /************************************************* 22 | Struct: CodecCtx 23 | Description: FFMpeg编解码器上下文 24 | *************************************************/ 25 | typedef struct { 26 | AVCodec *pCodec; //编解码器实例指针 27 | AVCodecContext *pCodecContext; //编解码器上下文,指定了编解码的参数 28 | AVCodecParserContext *pCodecParserCtx; //编解码解析器,从码流中截取完整的一个NAL Unit数据 29 | 30 | AVFrame *frame; //封装图像对象指针 31 | AVPacket pkt; //封装码流对象实例 32 | } CodecCtx; 33 | 34 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDecoding/VideoDecodingMain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "InputOutput.h" 4 | #include "Decoder.h" 5 | 6 | /************************************************* 7 | Function: hello 8 | Description: 输出提示信息和命令行格式 9 | Calls: 无 10 | Called By: main 11 | Input: 无 12 | Output: 无 13 | Return: 无 14 | *************************************************/ 15 | void hello() { 16 | printf("*********************************\n"); 17 | printf("VideoDecoding: A FFmpeg SDK demo.\nDeveloped by Yin Wenjie\n"); 18 | printf("*********************************\n"); 19 | printf("=================================\nCompulsory Paramaters:\n"); 20 | printf("Input YUV file name"); 21 | printf("\tOutput stream file name\n"); 22 | printf("*********************************\n"); 23 | } 24 | 25 | 26 | /************************************************* 27 | Function: write_out_yuv_frame 28 | Description: 将解码完成的YUV数据写出到文件 29 | Calls: 无 30 | Called By: main 31 | Input: (in)ctx : 获取AVFrame中的数据地址 32 | Output: (out)in_out : 写出到in_out指定的输出文件 33 | Return: 无 34 | *************************************************/ 35 | void write_out_yuv_frame(const CodecCtx &ctx, IOParam &in_out) { 36 | uint8_t **pBuf = ctx.frame->data; 37 | int *pStride = ctx.frame->linesize; 38 | 39 | for (int color_idx = 0; color_idx < 3; color_idx++) { 40 | int nWidth = color_idx == 0 ? ctx.frame->width : ctx.frame->width / 2; 41 | int nHeight = color_idx == 0 ? ctx.frame->height : ctx.frame->height / 2; 42 | for (int idx = 0; idx < nHeight; idx++) { 43 | fwrite(pBuf[color_idx], 1, nWidth, in_out.pFout); 44 | pBuf[color_idx] += pStride[color_idx]; 45 | } 46 | fflush(in_out.pFout); 47 | } 48 | } 49 | 50 | /************************************************* 51 | Function: main 52 | Description: 入口点函数 53 | *************************************************/ 54 | int main(int argc, char **argv) { 55 | uint8_t *pDataPtr = NULL; 56 | int uDataSize = 0; 57 | int got_picture, len; 58 | 59 | CodecCtx ctx; 60 | IOParam inputoutput; 61 | 62 | hello(); //输出提示信息 63 | 64 | Parse(argc, argv, inputoutput); //解析命令行参数 65 | 66 | Open_files(inputoutput); //打开输入输出文件 67 | 68 | uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; 69 | 70 | memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE); 71 | 72 | printf("Decode video file %s to %s\n", argv[1], argv[2]); 73 | 74 | Open_deocder(ctx); //打开编解码器各个组件 75 | 76 | while (1) { 77 | //将码流文件按某长度读入输入缓存区 78 | uDataSize = fread(inbuf, 1, INBUF_SIZE, inputoutput.pFin); 79 | if (0 == uDataSize) { 80 | break; 81 | } 82 | 83 | pDataPtr = inbuf; 84 | 85 | while (uDataSize > 0) { 86 | //解析缓存区中的数据为AVPacket对象,包含一个NAL Unit的数据 87 | len = av_parser_parse2(ctx.pCodecParserCtx, ctx.pCodecContext, 88 | &(ctx.pkt.data), &(ctx.pkt.size), 89 | pDataPtr, uDataSize, 90 | AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE); 91 | pDataPtr += len; 92 | uDataSize -= len; 93 | 94 | if (0 == ctx.pkt.size) { 95 | continue; 96 | } 97 | 98 | printf("Parse 1 packet. Packet pts: %d.\n", ctx.pkt.pts); 99 | 100 | //根据AVCodecContext的设置,解析AVPacket中的码流,输出到AVFrame 101 | int ret = avcodec_decode_video2(ctx.pCodecContext, ctx.frame, &got_picture, &(ctx.pkt)); 102 | if (ret < 0) { 103 | printf("Decode Error.\n"); 104 | return ret; 105 | } 106 | 107 | if (got_picture) { 108 | //获得一帧完整的图像,写出到输出文件 109 | write_out_yuv_frame(ctx, inputoutput); 110 | printf("Succeed to decode 1 frame! Frame pts: %d\n", ctx.frame->pts); 111 | } 112 | } //while(uDataSize > 0) 113 | } 114 | 115 | ctx.pkt.data = NULL; 116 | ctx.pkt.size = 0; 117 | while (1) { 118 | //将编码器中剩余的数据继续输出完 119 | int ret = avcodec_decode_video2(ctx.pCodecContext, ctx.frame, &got_picture, &(ctx.pkt)); 120 | if (ret < 0) { 121 | printf("Decode Error.\n"); 122 | return ret; 123 | } 124 | 125 | if (got_picture) { 126 | write_out_yuv_frame(ctx, inputoutput); 127 | printf("Flush Decoder: Succeed to decode 1 frame!\n"); 128 | } else { 129 | break; 130 | } 131 | } //while(1) 132 | 133 | //收尾工作 134 | Close_files(inputoutput); 135 | Close_decoder(ctx); 136 | 137 | return 1; 138 | } -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDemuxing/AVDecoder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "AVDecoder.h" 4 | 5 | 6 | int Get_format_from_sample_fmt(const char **fmt, enum AVSampleFormat sample_fmt) { 7 | int i; 8 | struct sample_fmt_entry { 9 | enum AVSampleFormat sample_fmt; 10 | const char *fmt_be, *fmt_le; 11 | } 12 | 13 | sample_fmt_entries[] = 14 | { 15 | {AV_SAMPLE_FMT_U8, "u8", "u8"}, 16 | {AV_SAMPLE_FMT_S16, "s16be", "s16le"}, 17 | {AV_SAMPLE_FMT_S32, "s32be", "s32le"}, 18 | {AV_SAMPLE_FMT_FLT, "f32be", "f32le"}, 19 | {AV_SAMPLE_FMT_DBL, "f64be", "f64le"}, 20 | }; 21 | *fmt = NULL; 22 | 23 | for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) { 24 | struct sample_fmt_entry *entry = &sample_fmt_entries[i]; 25 | if (sample_fmt == entry->sample_fmt) { 26 | *fmt = AV_NE(entry->fmt_be, entry->fmt_le); 27 | return 0; 28 | } 29 | } 30 | 31 | fprintf(stderr, 32 | "sample format %s is not supported as output format\n", 33 | av_get_sample_fmt_name(sample_fmt)); 34 | return -1; 35 | } 36 | 37 | int Decode_packet(IOFileName &files, DemuxingVideoAudioContex &va_ctx, int *got_frame, int cached) { 38 | int ret = 0; 39 | int decoded = va_ctx.pkt.size; 40 | static int video_frame_count = 0; 41 | static int audio_frame_count = 0; 42 | 43 | *got_frame = 0; 44 | 45 | if (va_ctx.pkt.stream_index == va_ctx.video_stream_idx) { 46 | printf("Video packet pts: %d\n", va_ctx.pkt.pts); 47 | /* decode video frame */ 48 | ret = avcodec_decode_video2(va_ctx.video_dec_ctx, va_ctx.frame, got_frame, &va_ctx.pkt); 49 | if (ret < 0) { 50 | printf("Error decoding video frame (%d)\n", ret); 51 | return ret; 52 | } 53 | 54 | if (*got_frame) { 55 | if (va_ctx.frame->width != va_ctx.width || va_ctx.frame->height != va_ctx.height || 56 | va_ctx.frame->format != va_ctx.pix_fmt) { 57 | /* To handle this change, one could call av_image_alloc again and 58 | * decode the following frames into another rawvideo file. */ 59 | printf("Error: Width, height and pixel format have to be " 60 | "constant in a rawvideo file, but the width, height or " 61 | "pixel format of the input video changed:\n" 62 | "old: width = %d, height = %d, format = %s\n" 63 | "new: width = %d, height = %d, format = %s\n", 64 | va_ctx.width, va_ctx.height, av_get_pix_fmt_name((AVPixelFormat) (va_ctx.pix_fmt)), 65 | va_ctx.frame->width, va_ctx.frame->height, 66 | av_get_pix_fmt_name((AVPixelFormat) va_ctx.frame->format)); 67 | return -1; 68 | } 69 | 70 | printf("video_frame%s n:%d coded_n:%d pts:%s\n", cached ? "(cached)" : "", video_frame_count++, 71 | va_ctx.frame->coded_picture_number, va_ctx.frame->pts); 72 | 73 | /* copy decoded frame to destination buffer: 74 | * this is required since rawvideo expects non aligned data */ 75 | av_image_copy(va_ctx.video_dst_data, va_ctx.video_dst_linesize, 76 | (const uint8_t **) (va_ctx.frame->data), va_ctx.frame->linesize, 77 | va_ctx.pix_fmt, va_ctx.width, va_ctx.height); 78 | 79 | /* write to rawvideo file */ 80 | fwrite(va_ctx.video_dst_data[0], 1, va_ctx.video_dst_bufsize, files.video_dst_file); 81 | } 82 | } else if (va_ctx.pkt.stream_index == va_ctx.audio_stream_idx) { 83 | /* decode audio frame */ 84 | ret = avcodec_decode_audio4(va_ctx.audio_dec_ctx, va_ctx.frame, got_frame, &va_ctx.pkt); 85 | if (ret < 0) { 86 | printf("Error decoding audio frame (%d)\n", ret); 87 | return ret; 88 | } 89 | /* Some audio decoders decode only part of the packet, and have to be 90 | * called again with the remainder of the packet data. 91 | * Sample: fate-suite/lossless-audio/luckynight-partial.shn 92 | * Also, some decoders might over-read the packet. */ 93 | decoded = FFMIN(ret, va_ctx.pkt.size); 94 | 95 | if (*got_frame) { 96 | size_t unpadded_linesize = 97 | va_ctx.frame->nb_samples * av_get_bytes_per_sample((AVSampleFormat) va_ctx.frame->format); 98 | // printf("audio_frame%s n:%d nb_samples:%d pts:%s\n", cached ? "(cached)" : "", audio_frame_count++, va_ctx.frame->nb_samples, va_ctx.frame->pts); 99 | 100 | /* Write the raw audio data samples of the first plane. This works 101 | * fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However, 102 | * most audio decoders output planar audio, which uses a separate 103 | * plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P). 104 | * In other words, this code will write only the first audio channel 105 | * in these cases. 106 | * You should use libswresample or libavfilter to convert the frame 107 | * to packed data. */ 108 | fwrite(va_ctx.frame->extended_data[0], 1, unpadded_linesize, files.audio_dst_file); 109 | } 110 | } 111 | 112 | /* If we use frame reference counting, we own the data and need 113 | * to de-reference it when we don't use it anymore */ 114 | if (*got_frame && files.refcount) 115 | av_frame_unref(va_ctx.frame); 116 | 117 | return decoded; 118 | } 119 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDemuxing/AVDecoder.h: -------------------------------------------------------------------------------- 1 | #ifndef _AV_DECODER_H_ 2 | #define _AV_DECODER_H_ 3 | 4 | #include "IOFile.h" 5 | #include "DemuxingContext.h" 6 | 7 | /************************************************* 8 | Function: Get_format_from_sample_fmt 9 | Description: 获取音频采样格式 10 | Calls: FFMpeg API 11 | Called By: main 12 | Input: (in)sample_fmt : 采样格式结构体 13 | Output: (out)fmt : 标识采样格式的字符串 14 | Return: 错误码 15 | *************************************************/ 16 | int Get_format_from_sample_fmt(const char **fmt, enum AVSampleFormat sample_fmt); 17 | 18 | /************************************************* 19 | Function: Decode_packet 20 | Description: 解码一个音频或视频数据包 21 | Calls: FFMpeg API 22 | Called By: main 23 | Input: (in)files : 输入参数 24 | (in)va_ctx : 编码器上下文部件 25 | (in)cached : 是否处理解码器中的缓存数据 26 | Output: (out)got_frame : 完整解码一帧标识 27 | Return: 错误码 28 | *************************************************/ 29 | int Decode_packet(IOFileName &files, DemuxingVideoAudioContex &va_ctx, int *got_frame, int cached); 30 | 31 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDemuxing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(.) 2 | add_executable(VideoDemuxingMain VideoDemuxingMain.cpp AVDecoder.cpp DemuxingContext.cpp) 3 | target_link_libraries(VideoDemuxingMain ${LINK_LIBS} ${FFMPEG_LIBS}) -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDemuxing/DemuxingContext.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "DemuxingContext.h" 3 | 4 | 5 | static int open_codec_context(IOFileName &files, DemuxingVideoAudioContex &va_ctx, enum AVMediaType type) { 6 | int ret, stream_index; 7 | AVStream *st; 8 | AVCodecContext *dec_ctx = NULL; 9 | AVCodec *dec = NULL; 10 | AVDictionary *opts = NULL; 11 | 12 | ret = av_find_best_stream(va_ctx.fmt_ctx, type, -1, -1, NULL, 0); 13 | if (ret < 0) { 14 | fprintf(stderr, "Could not find %s stream in input file '%s'\n", av_get_media_type_string(type), 15 | files.src_filename); 16 | return ret; 17 | } else { 18 | stream_index = ret; 19 | st = va_ctx.fmt_ctx->streams[stream_index]; 20 | if (type == AVMEDIA_TYPE_VIDEO) { 21 | printf("Video stream time base: {%d ,%d}\n", st->time_base.num, st->time_base.den); 22 | } 23 | 24 | /* find decoder for the stream */ 25 | dec_ctx = st->codec; 26 | dec = avcodec_find_decoder(dec_ctx->codec_id); 27 | if (!dec) { 28 | fprintf(stderr, "Failed to find %s codec\n", av_get_media_type_string(type)); 29 | return AVERROR(EINVAL); 30 | } 31 | 32 | /* Init the decoders, with or without reference counting */ 33 | av_dict_set(&opts, "refcounted_frames", files.refcount ? "1" : "0", 0); 34 | if ((ret = avcodec_open2(dec_ctx, dec, &opts)) < 0) { 35 | fprintf(stderr, "Failed to open %s codec\n", av_get_media_type_string(type)); 36 | return ret; 37 | } 38 | 39 | switch (type) { 40 | case AVMEDIA_TYPE_VIDEO: 41 | va_ctx.video_stream_idx = stream_index; 42 | va_ctx.video_stream = va_ctx.fmt_ctx->streams[stream_index]; 43 | va_ctx.video_dec_ctx = va_ctx.video_stream->codec; 44 | break; 45 | case AVMEDIA_TYPE_AUDIO: 46 | va_ctx.audio_stream_idx = stream_index; 47 | va_ctx.audio_stream = va_ctx.fmt_ctx->streams[stream_index]; 48 | va_ctx.audio_dec_ctx = va_ctx.audio_stream->codec; 49 | break; 50 | default: 51 | fprintf(stderr, "Error: unsupported MediaType: %s\n", av_get_media_type_string(type)); 52 | return -1; 53 | } 54 | } 55 | 56 | return 0; 57 | } 58 | 59 | int InitDemuxContext(IOFileName &files, DemuxingVideoAudioContex &va_ctx) { 60 | int ret = 0, width, height; 61 | 62 | /* register all formats and codecs */ 63 | av_register_all(); 64 | 65 | /* open input file, and allocate format context */ 66 | if (avformat_open_input(&(va_ctx.fmt_ctx), files.src_filename, NULL, NULL) < 0) { 67 | fprintf(stderr, "Could not open source file %s\n", files.src_filename); 68 | return -1; 69 | } 70 | 71 | /* retrieve stream information */ 72 | if (avformat_find_stream_info(va_ctx.fmt_ctx, NULL) < 0) { 73 | fprintf(stderr, "Could not find stream information\n"); 74 | return -1; 75 | } 76 | 77 | if (open_codec_context(files, va_ctx, AVMEDIA_TYPE_VIDEO) >= 0) { 78 | files.video_dst_file = fopen(files.video_dst_filename, "wb"); 79 | if (!files.video_dst_file) { 80 | fprintf(stderr, "Could not open destination file %s\n", files.video_dst_filename); 81 | return -1; 82 | } 83 | 84 | /* allocate image where the decoded image will be put */ 85 | va_ctx.width = va_ctx.video_dec_ctx->width; 86 | va_ctx.height = va_ctx.video_dec_ctx->height; 87 | va_ctx.pix_fmt = va_ctx.video_dec_ctx->pix_fmt; 88 | ret = av_image_alloc(va_ctx.video_dst_data, va_ctx.video_dst_linesize, va_ctx.width, va_ctx.height, 89 | va_ctx.pix_fmt, 1); 90 | if (ret < 0) { 91 | fprintf(stderr, "Could not allocate raw video buffer\n"); 92 | return -1; 93 | } 94 | va_ctx.video_dst_bufsize = ret; 95 | } 96 | 97 | if (open_codec_context(files, va_ctx, AVMEDIA_TYPE_AUDIO) >= 0) { 98 | files.audio_dst_file = fopen(files.audio_dst_filename, "wb"); 99 | if (!files.audio_dst_file) { 100 | fprintf(stderr, "Could not open destination file %s\n", files.audio_dst_filename); 101 | return -1; 102 | } 103 | } 104 | 105 | if (va_ctx.video_stream) { 106 | printf("Demuxing video from file '%s' into '%s'\n", files.src_filename, files.video_dst_filename); 107 | } 108 | 109 | if (va_ctx.audio_stream) { 110 | printf("Demuxing audio from file '%s' into '%s'\n", files.src_filename, files.audio_dst_filename); 111 | } 112 | 113 | /* dump input information to stderr */ 114 | av_dump_format(va_ctx.fmt_ctx, 0, files.src_filename, 0); 115 | 116 | if (!va_ctx.audio_stream && !va_ctx.video_stream) { 117 | fprintf(stderr, "Could not find audio or video stream in the input, aborting\n"); 118 | return -1; 119 | } 120 | 121 | return 0; 122 | } 123 | 124 | void CloseDemuxContext(IOFileName &files, DemuxingVideoAudioContex &va_ctx) { 125 | avcodec_close(va_ctx.video_dec_ctx); 126 | avcodec_close(va_ctx.audio_dec_ctx); 127 | avformat_close_input(&(va_ctx.fmt_ctx)); 128 | av_frame_free(&va_ctx.frame); 129 | av_free(va_ctx.video_dst_data[0]); 130 | 131 | if (files.video_dst_file) 132 | fclose(files.video_dst_file); 133 | if (files.audio_dst_file) 134 | fclose(files.audio_dst_file); 135 | } 136 | 137 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDemuxing/DemuxingContext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef _DEMUXING_CONTEXT_H_ 3 | #define _DEMUXING_CONTEXT_H_ 4 | 5 | #include "VideoDemuxingHeader.h" 6 | #include "IOFile.h" 7 | 8 | /************************************************* 9 | Struct: DemuxingVideoAudioContex 10 | Description: 保存解复用器和解码器的上下文组件 11 | *************************************************/ 12 | typedef struct { 13 | AVFormatContext *fmt_ctx; 14 | AVCodecContext *video_dec_ctx, *audio_dec_ctx; 15 | AVStream *video_stream, *audio_stream; 16 | AVFrame *frame; 17 | AVPacket pkt; 18 | 19 | int video_stream_idx, audio_stream_idx; 20 | int width, height; 21 | 22 | uint8_t *video_dst_data[4]; 23 | int video_dst_linesize[4]; 24 | int video_dst_bufsize; 25 | enum AVPixelFormat pix_fmt; 26 | } DemuxingVideoAudioContex; 27 | 28 | /************************************************* 29 | Function: InitDemuxContext 30 | Description: 根据输入参数设置编码器上下文 31 | Calls: open_codec_context 32 | Called By: main 33 | Input: (in)files : 输入参数 34 | Output: (out)va_ctx : 编码器上下文部件 35 | Return: 错误码 36 | *************************************************/ 37 | int InitDemuxContext(IOFileName &files, DemuxingVideoAudioContex &va_ctx); 38 | 39 | /************************************************* 40 | Function: CloseDemuxContext 41 | Description: 关闭编码器上下文 42 | Calls: 无 43 | Called By: main 44 | Input: (in)files : 输入参数 45 | Output: (out)va_ctx : 编码器上下文部件 46 | Return: 无 47 | *************************************************/ 48 | void CloseDemuxContext(IOFileName &files, DemuxingVideoAudioContex &va_ctx); 49 | 50 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDemuxing/IOFile.h: -------------------------------------------------------------------------------- 1 | #ifndef _IOFILE_H_ 2 | #define _IOFILE_H_ 3 | 4 | /************************************************* 5 | Struct: IOParam 6 | Description: 接收命令行参数 7 | *************************************************/ 8 | typedef struct { 9 | const char *src_filename; 10 | const char *video_dst_filename; 11 | const char *audio_dst_filename; 12 | 13 | int refcount; 14 | 15 | FILE *video_dst_file; 16 | FILE *audio_dst_file; 17 | } IOFileName; 18 | 19 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDemuxing/VideoDemuxingHeader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef _VIDEO_DEMUXING_HEADER_H 3 | #define _VIDEO_DEMUXING_HEADER_H 4 | 5 | extern "C" 6 | { 7 | #include "libavutil/imgutils.h" 8 | #include "libavutil/samplefmt.h" 9 | #include "libavformat/avformat.h" 10 | } 11 | 12 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoDemuxing/VideoDemuxingMain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "DemuxingContext.h" 4 | #include "AVDecoder.h" 5 | 6 | #define MAX_NUM_TO_DECODE 300 7 | 8 | /************************************************* 9 | Function: hello 10 | Description: 输出提示信息和命令行格式 11 | Calls: 无 12 | Called By: main 13 | Input: 无 14 | Output: 无 15 | Return: 无 16 | *************************************************/ 17 | static int hello(IOFileName &files, int argc, char **argv) { 18 | if (argc != 4 && argc != 5) { 19 | fprintf(stderr, "usage: %s [-refcount] input_file video_output_file audio_output_file\n" 20 | "API example program to show how to read frames from an input file.\n" 21 | "This program reads frames from a file, decodes them, and writes decoded\n" 22 | "video frames to a rawvideo file named video_output_file, and decoded\n" 23 | "audio frames to a rawaudio file named audio_output_file.\n\n" 24 | "If the -refcount option is specified, the program use the\n" 25 | "reference counting frame system which allows keeping a copy of\n" 26 | "the data for longer than one decode call.\n" 27 | "\n", argv[0]); 28 | return -1; 29 | } 30 | 31 | if (argc == 5 && !strcmp(argv[1], "-refcount")) { 32 | files.refcount = 1; 33 | argv++; 34 | } 35 | 36 | files.src_filename = argv[1]; 37 | files.video_dst_filename = argv[2]; 38 | files.audio_dst_filename = argv[3]; 39 | 40 | return 0; 41 | } 42 | 43 | /************************************************* 44 | Function: main 45 | Description: 入口点函数 46 | *************************************************/ 47 | int main(int argc, char **argv) { 48 | int ret = 0, got_frame, frameNum = 0; 49 | IOFileName files = {NULL}; 50 | DemuxingVideoAudioContex va_ctx = {NULL}; 51 | 52 | hello(files, argc, argv); //输出提示信息 53 | 54 | if (InitDemuxContext(files, va_ctx) < 0) //初始化解复用上下文结构体,包括注册组件、打开输入输出文件、查找音视频流等 55 | { 56 | goto end; 57 | } 58 | 59 | va_ctx.frame = av_frame_alloc(); //分配AVFrame结构对象 60 | if (!va_ctx.frame) { 61 | fprintf(stderr, "Could not allocate frame\n"); 62 | ret = AVERROR(ENOMEM); 63 | goto end; 64 | } 65 | 66 | /* initialize packet, set data to NULL, let the demuxer fill it */ 67 | av_init_packet(&va_ctx.pkt); //初始化AVPacket对象 68 | va_ctx.pkt.data = NULL; 69 | va_ctx.pkt.size = 0; 70 | 71 | /* read frames from the file */ 72 | while (av_read_frame(va_ctx.fmt_ctx, &va_ctx.pkt) >= 0) //从输入程序中读取一个包的数据 73 | { 74 | if (frameNum++ > MAX_NUM_TO_DECODE) { 75 | break; 76 | } 77 | do { 78 | ret = Decode_packet(files, va_ctx, &got_frame, 0); //解码这个包 79 | if (ret < 0) 80 | break; 81 | va_ctx.pkt.data += ret; 82 | va_ctx.pkt.size -= ret; 83 | } while (va_ctx.pkt.size > 0); 84 | } 85 | 86 | /* flush cached frames */ 87 | va_ctx.pkt.data = NULL; 88 | va_ctx.pkt.size = 0; 89 | do { 90 | Decode_packet(files, va_ctx, &got_frame, 1); //解码缓存中未输出的包 91 | } while (got_frame); 92 | 93 | printf("Demuxing succeeded.\n"); 94 | 95 | if (va_ctx.video_stream) { 96 | printf("Play the output video file with the command:\n" 97 | "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n", 98 | av_get_pix_fmt_name(va_ctx.pix_fmt), va_ctx.width, va_ctx.height, files.video_dst_filename); 99 | } 100 | 101 | if (va_ctx.audio_stream) { 102 | enum AVSampleFormat sfmt = va_ctx.audio_dec_ctx->sample_fmt; 103 | int n_channels = va_ctx.audio_dec_ctx->channels; 104 | const char *fmt; 105 | 106 | if (av_sample_fmt_is_planar(sfmt)) { 107 | const char *packed = av_get_sample_fmt_name(sfmt); 108 | printf("Warning: the sample format the decoder produced is planar " 109 | "(%s). This example will output the first channel only.\n", 110 | packed ? packed : "?"); 111 | sfmt = av_get_packed_sample_fmt(sfmt); 112 | n_channels = 1; 113 | } 114 | 115 | if ((ret = Get_format_from_sample_fmt(&fmt, sfmt)) < 0) 116 | goto end; 117 | 118 | printf("Play the output audio file with the command:\n" 119 | "ffplay -f %s -ac %d -ar %d %s\n", 120 | fmt, n_channels, va_ctx.audio_dec_ctx->sample_rate, 121 | files.audio_dst_filename); 122 | } 123 | 124 | end: 125 | CloseDemuxContext(files, va_ctx); 126 | 127 | return ret < 0; 128 | } -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncoding/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(VideoEncodingMain VideoEncodingMain.cpp Encoder.cpp InputOutput.cpp) 2 | target_link_libraries(VideoEncodingMain ${LINK_LIBS} ${FFMPEG_LIBS}) -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncoding/Encoder.cpp: -------------------------------------------------------------------------------- 1 | #include "Encoder.h" 2 | #include "VideoEncodingHeader.h" 3 | 4 | /************************************************* 5 | Function: setContext 6 | Description: 根据输入参数设置编码器上下文 7 | Calls: av_opt_set 8 | Called By: Open_encoder 9 | Input: (in)io_param : 命令行输入参数 10 | Output: (out)ctx : 编码器上下文部件 11 | Return: 无 12 | *************************************************/ 13 | void setContext(CodecCtx &ctx, IOParam io_param) { 14 | /* put sample parameters */ 15 | ctx.c->bit_rate = io_param.nBitRate; 16 | 17 | /* resolution must be a multiple of two */ 18 | ctx.c->width = io_param.nImageWidth; 19 | ctx.c->height = io_param.nImageHeight; 20 | 21 | /* frames per second */ 22 | AVRational rational = {1, 25}; 23 | ctx.c->time_base = rational; 24 | 25 | ctx.c->gop_size = io_param.nGOPSize; 26 | ctx.c->max_b_frames = io_param.nMaxBFrames; 27 | ctx.c->pix_fmt = AV_PIX_FMT_YUV420P; 28 | 29 | av_opt_set(ctx.c->priv_data, "preset", "slow", 0); 30 | } 31 | 32 | bool Open_encoder(CodecCtx &ctx, IOParam io_param) { 33 | int ret; 34 | 35 | avcodec_register_all(); //注册所有所需的音视频编解码器 36 | 37 | /* find the mpeg1 video encoder */ 38 | ctx.codec = avcodec_find_encoder(AV_CODEC_ID_H264); //根据CODEC_ID查找编解码器对象实例的指针 39 | if (!ctx.codec) { 40 | fprintf(stderr, "Codec not found\n"); 41 | return false; 42 | } 43 | 44 | ctx.c = avcodec_alloc_context3(ctx.codec); //分配AVCodecContext实例 45 | if (!ctx.c) { 46 | fprintf(stderr, "Could not allocate video codec context\n"); 47 | return false; 48 | } 49 | 50 | setContext(ctx, io_param); //设置编码器的上下文 51 | 52 | /* open it */ 53 | if (avcodec_open2(ctx.c, ctx.codec, NULL) < 0) //根据编码器上下文打开编码器 54 | { 55 | fprintf(stderr, "Could not open codec\n"); 56 | exit(1); 57 | } 58 | 59 | ctx.frame = av_frame_alloc(); //分配AVFrame对象 60 | if (!ctx.frame) { 61 | fprintf(stderr, "Could not allocate video frame\n"); 62 | return false; 63 | } 64 | ctx.frame->format = ctx.c->pix_fmt; 65 | ctx.frame->width = ctx.c->width; 66 | ctx.frame->height = ctx.c->height; 67 | 68 | /* the image can be allocated by any means and av_image_alloc() is 69 | * just the most convenient way if av_malloc() is to be used */ 70 | //分配AVFrame所包含的像素存储空间 71 | ret = av_image_alloc(ctx.frame->data, ctx.frame->linesize, ctx.c->width, ctx.c->height, ctx.c->pix_fmt, 32); 72 | if (ret < 0) { 73 | fprintf(stderr, "Could not allocate raw picture buffer\n"); 74 | return false; 75 | } 76 | 77 | return true; 78 | } 79 | 80 | void Close_encoder(CodecCtx &ctx) { 81 | avcodec_close(ctx.c); 82 | av_free(ctx.c); 83 | av_freep(&(ctx.frame->data[0])); 84 | av_frame_free(&(ctx.frame)); 85 | } 86 | 87 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncoding/Encoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef _ENCODER_H_ 3 | #define _ENCODER_H_ 4 | 5 | #include "VideoEncodingHeader.h" 6 | #include "InputOutput.h" 7 | 8 | /************************************************* 9 | Function: Open_encoder 10 | Description: 根据输入参数配置AVCodecContext,并打开AVCodec等结构对象 11 | Calls: avcodec_register_all 12 | avcodec_find_encoder 13 | avcodec_alloc_context3 14 | setContext 15 | avcodec_open2 16 | av_frame_alloc 17 | av_image_alloc 18 | Called By: main 19 | Input: (in)io_param : 保存输入输出文件的指针 20 | Output: (out)ctx : 包含编码器部件的结构对象 21 | Return: true : 打开各个编码器部件成功 22 | false : 打开编码器部件失败 23 | *************************************************/ 24 | bool Open_encoder(CodecCtx &ctx, IOParam io_param); 25 | 26 | /************************************************* 27 | Function: Close_encoder 28 | Description: 关闭编码器上下文部件 29 | Calls: avcodec_close 30 | av_free 31 | av_freep 32 | av_frame_free 33 | Called By: main 34 | Input: (in)ctx : 编码器上下文部件 35 | Output: 无 36 | Return: 无 37 | *************************************************/ 38 | void Close_encoder(CodecCtx &ctx); 39 | 40 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncoding/InputOutput.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "InputOutput.h" 4 | 5 | bool Parse_input_param(int argc, char **argv, IOParam &io_param) { 6 | bool bNameInFound = false, bNameOutFound = false, bWidthFound = false, bHeightFound = false, bBitrateFound = false, bTotalFrames = false; 7 | 8 | io_param.nFrameRate = 25; 9 | io_param.nGOPSize = 10; 10 | io_param.nMaxBFrames = 1; 11 | 12 | //根据命令行解析输入参数 13 | for (int idx = 1; idx < argc; idx++) { 14 | if (!strcmp(argv[idx], "-i")) { 15 | io_param.pNameIn = argv[idx + 1]; 16 | bNameInFound = true; 17 | } else if (!strcmp(argv[idx], "-o")) { 18 | io_param.pNameOut = argv[idx + 1]; 19 | bNameOutFound = true; 20 | } else if (!strcmp(argv[idx], "-w")) { 21 | io_param.nImageWidth = atoi(argv[idx + 1]); 22 | bWidthFound = true; 23 | } else if (!strcmp(argv[idx], "-h")) { 24 | io_param.nImageHeight = atoi(argv[idx + 1]); 25 | bHeightFound = true; 26 | } else if (!strcmp(argv[idx], "-br")) { 27 | io_param.nBitRate = atol(argv[idx + 1]); 28 | bBitrateFound = true; 29 | } else if (!strcmp(argv[idx], "-tf")) { 30 | io_param.nTotalFrames = atoi(argv[idx + 1]); 31 | bTotalFrames = true; 32 | } else if (!strcmp(argv[idx], "-fr")) { 33 | io_param.nFrameRate = atoi(argv[idx + 1]); 34 | } else if (!strcmp(argv[idx], "-gs")) { 35 | io_param.nGOPSize = atoi(argv[idx + 1]); 36 | } else if (!strcmp(argv[idx], "-mbs")) { 37 | io_param.nMaxBFrames = atoi(argv[idx + 1]); 38 | } 39 | } //for (int idx = 1; idx < argc; idx++) 40 | 41 | //必要信息缺失,返回错误 42 | if (!bNameInFound) { 43 | printf("Error: Cannot find input file name.\n"); 44 | return false; 45 | } 46 | if (!bNameOutFound) { 47 | printf("Error: Cannot find output file name.\n"); 48 | return false; 49 | } 50 | if (!bWidthFound) { 51 | printf("Error: Cannot find image width.\n"); 52 | return false; 53 | } 54 | if (!bHeightFound) { 55 | printf("Error: Cannot find image height.\n"); 56 | return false; 57 | } 58 | if (!bBitrateFound) { 59 | printf("Error: Cannot find bitrate.\n"); 60 | return false; 61 | } 62 | 63 | printf("Paramaters parsing OK!\n"); 64 | return true; 65 | } 66 | 67 | bool Open_file(IOParam &io_param) { 68 | //打开输入文件 69 | io_param.pFin = fopen(io_param.pNameIn, "rb"); 70 | if (!(io_param.pFin)) { 71 | fprintf(stderr, "Could not open %s\n", io_param.pNameIn); 72 | return false; 73 | } 74 | 75 | //打开输出文件 76 | io_param.pFout = fopen(io_param.pNameOut, "wb"); 77 | if (!(io_param.pFin)) { 78 | fprintf(stderr, "Could not open %s\n", io_param.pNameOut); 79 | return false; 80 | } 81 | 82 | return true; 83 | } 84 | 85 | void Close_file(IOParam &io_param) { 86 | fclose(io_param.pFin); 87 | fclose(io_param.pFout); 88 | } 89 | 90 | int Read_yuv_data(CodecCtx &ctx, IOParam &io_param, int color_plane) { 91 | int frame_height = color_plane == 0 ? ctx.frame->height : ctx.frame->height / 2; 92 | int frame_width = color_plane == 0 ? ctx.frame->width : ctx.frame->width / 2; 93 | int frame_size = frame_width * frame_height; 94 | int frame_stride = ctx.frame->linesize[color_plane]; 95 | 96 | if (frame_width == frame_stride) { 97 | //宽度和跨度相等,像素信息连续存放 98 | fread(ctx.frame->data[color_plane], 1, frame_size, io_param.pFin); 99 | } else { 100 | //宽度小于跨度,像素信息保存空间之间存在间隔 101 | for (int row_idx = 0; row_idx < frame_height; row_idx++) { 102 | fread(ctx.frame->data[color_plane] + row_idx * frame_stride, 1, frame_width, io_param.pFin); 103 | } 104 | } 105 | 106 | return frame_size; 107 | } 108 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncoding/InputOutput.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef _INPUT_OUTPUT_H_ 3 | #define _INPUT_OUTPUT_H_ 4 | 5 | #include 6 | #include 7 | #include "VideoEncodingHeader.h" 8 | 9 | /************************************************* 10 | Struct: IOParam 11 | Description: 接收命令行参数 12 | *************************************************/ 13 | typedef struct { 14 | FILE *pFin; //输入的YUV文件指针 15 | FILE *pFout; //输出的码流文件指针 16 | 17 | char *pNameIn; //输入YUV文件名 18 | char *pNameOut; //输出码流文件名 19 | 20 | uint16_t nImageWidth; //图像宽度 21 | uint16_t nImageHeight; //图像高度 22 | 23 | uint16_t nFrameRate; //编码帧率 24 | uint64_t nBitRate; //编码码率 25 | uint16_t nGOPSize; //一个GOP大小 26 | uint16_t nMaxBFrames; //最大B帧数量 27 | uint16_t nTotalFrames; //编码总帧数 28 | } IOParam; 29 | 30 | /************************************************* 31 | Function: Parse_input_param 32 | Description: 解析命令行传入的参数 33 | Calls: 无 34 | Called By: main 35 | Input: (in)argc : 默认命令行参数 36 | (in)argv : 默认命令行参数 37 | Output: (out)io_param : 解析命令行的结果 38 | Return: true : 命令行解析正确 39 | false : 命令行解析错误 40 | *************************************************/ 41 | bool Parse_input_param(int argc, char **argv, IOParam &io_param); 42 | 43 | /************************************************* 44 | Function: Open_file 45 | Description: 打开输入输出文件 46 | Calls: 无 47 | Called By: main 48 | Input: (in/out)io_param : 传入文件的路径 49 | Output: (in/out)io_param : 用结构体成员保存打开文件的指针 50 | Return: true : 打开输入输出文件正确 51 | false : 打开输入输出文件错误 52 | *************************************************/ 53 | bool Open_file(IOParam &io_param); 54 | 55 | /************************************************* 56 | Function: Close_file 57 | Description: 关闭输入输出文件 58 | Calls: 无 59 | Called By: main 60 | Input: (in)io_param : 保存输入输出文件的指针 61 | Output: 无 62 | Return: 无 63 | *************************************************/ 64 | void Close_file(IOParam &io_param); 65 | 66 | /************************************************* 67 | Function: Read_yuv_data 68 | Description: 从打开的输入文件中读取yuv数据,并保存到CodecCtx::AVFrame 69 | Calls: 无 70 | Called By: main 71 | Input: (in)io_param : 保存输入输出文件的指针 72 | (in)color_plane : 颜色分量标识:0-Y, 1-U, 2-V 73 | Output: (out)ctx : 保存AVFrame的结构实例 74 | Return: 无 75 | *************************************************/ 76 | int Read_yuv_data(CodecCtx &ctx, IOParam &io_param, int color_plane); 77 | 78 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncoding/VideoEncodingHeader.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/Tutorial/SDK_Demo/VideoEncoding/VideoEncodingHeader.h -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncoding/VideoEncodingMain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "VideoEncodingHeader.h" 3 | #include "Encoder.h" 4 | #include "InputOutput.h" 5 | 6 | /************************************************* 7 | Function: hello 8 | Description: 输出提示信息和命令行格式 9 | Calls: 无 10 | Called By: main 11 | Input: 无 12 | Output: 无 13 | Return: 无 14 | *************************************************/ 15 | void hello() { 16 | printf("*********************************\n"); 17 | printf("VideoEncoding: A FFmpeg SDK demo.\nDeveloped by Yin Wenjie\n"); 18 | printf("*********************************\n"); 19 | printf("=================================\nCompulsory Paramaters:\n"); 20 | printf("\t-i:\tInput YUV file name\n"); 21 | printf("\t-o:\tOutput stream file name\n"); 22 | printf("\t-w:\tInput frame width\n"); 23 | printf("\t-h:\tInput frame height\n"); 24 | printf("\t-br:\tInput bit rate\n"); 25 | printf("\t-tf:\tTotal frames to encode\n"); 26 | printf("=================================\nOptional Paramaters:\n"); 27 | printf("\t-fr:\tFrame rate\n"); 28 | printf("\t-gs:\tGOP size\n"); 29 | printf("\t-mbf:\tMax B Frames\n"); 30 | printf("*********************************\n"); 31 | } 32 | 33 | /************************************************* 34 | Function: main 35 | Description: 入口点函数 36 | *************************************************/ 37 | int main(int argc, char **argv) { 38 | hello(); //输出提示信息 39 | 40 | IOParam io_param; 41 | if (Parse_input_param(argc, argv, io_param))//读取并解析命令行参数 42 | { 43 | printf("Error: Incomplete input parameters. Please check the command line."); 44 | } 45 | 46 | 47 | CodecCtx ctx = {NULL, NULL, NULL}; 48 | int frameIdx, packetIdx = 0, ret, got_output; 49 | 50 | Open_file(io_param); //打开输入输出文件 51 | Open_encoder(ctx, io_param); //根据输入参数设置并打开编码器各个部件 52 | 53 | /* encode 1 second of video */ 54 | for (frameIdx = 0; frameIdx < io_param.nTotalFrames; frameIdx++) { 55 | av_init_packet(&(ctx.pkt)); //初始化AVPacket实例 56 | ctx.pkt.data = NULL; // packet data will be allocated by the encoder 57 | ctx.pkt.size = 0; 58 | 59 | fflush(stdout); 60 | 61 | Read_yuv_data(ctx, io_param, 0); //Y分量 62 | Read_yuv_data(ctx, io_param, 1); //U分量 63 | Read_yuv_data(ctx, io_param, 2); //V分量 64 | 65 | ctx.frame->pts = frameIdx; 66 | 67 | /* encode the image */ 68 | ret = avcodec_encode_video2(ctx.c, &(ctx.pkt), ctx.frame, &got_output); //将AVFrame中的像素信息编码为AVPacket中的码流 69 | if (ret < 0) { 70 | fprintf(stderr, "Error encoding frame\n"); 71 | exit(1); 72 | } 73 | printf("Encode frame index: %d, frame pts: %d.\n", frameIdx, ctx.frame->pts); 74 | if (got_output) { 75 | //获得一个完整的码流包 76 | printf("Write packets %3d (size=%5d). Packet pts: %d\n", packetIdx++, ctx.pkt.size, ctx.pkt.pts); 77 | fwrite(ctx.pkt.data, 1, ctx.pkt.size, io_param.pFout); 78 | av_packet_unref(&(ctx.pkt)); 79 | } 80 | } //for (frameIdx = 0; frameIdx < io_param.nTotalFrames; frameIdx++) 81 | 82 | /* get the delayed frames */ 83 | for (got_output = 1; got_output; frameIdx++) { 84 | fflush(stdout); 85 | 86 | ret = avcodec_encode_video2(ctx.c, &(ctx.pkt), NULL, &got_output); //输出编码器中剩余的码流 87 | if (ret < 0) { 88 | fprintf(stderr, "Error encoding frame\n"); 89 | exit(1); 90 | } 91 | 92 | if (got_output) { 93 | printf("Cached frames: Write packets %3d (size=%5d). Packet pts: %d\n", packetIdx++, ctx.pkt.size, 94 | ctx.pkt.pts); 95 | fwrite(ctx.pkt.data, 1, ctx.pkt.size, io_param.pFout); 96 | av_packet_unref(&(ctx.pkt)); 97 | } 98 | } //for (got_output = 1; got_output; frameIdx++) 99 | 100 | //结尾处理 101 | Close_file(io_param); 102 | Close_encoder(ctx); 103 | 104 | return 0; 105 | } -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(.) 2 | add_executable(EncodingMuxingMain EncodingMuxingMain.cpp CoderMuxer.cpp EncodingMuxingAudio.cpp EncodingMuxingVideo.cpp Stream.cpp) 3 | target_link_libraries(EncodingMuxingMain ${LINK_LIBS} ${FFMPEG_LIBS}) -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/CoderMuxer.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "CoderMuxer.h" 3 | 4 | 5 | int Open_coder_muxer(AVOutputFormat **fmt, AVFormatContext **oc, const char *filename) { 6 | /* Initialize libavcodec, and register all codecs and formats. */ 7 | av_register_all(); 8 | 9 | /* allocate the output media context */ 10 | avformat_alloc_output_context2(oc, NULL, NULL, filename); 11 | if (!oc) { 12 | printf("Could not deduce output format from file extension: using MPEG.\n"); 13 | avformat_alloc_output_context2(oc, NULL, "mpeg", filename); 14 | } 15 | if (!oc) { 16 | return 1; 17 | } 18 | 19 | *fmt = (*oc)->oformat; 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/CoderMuxer.h: -------------------------------------------------------------------------------- 1 | #ifndef _CODER_MUXER_H_ 2 | #define _CODER_MUXER_H_ 3 | 4 | int Open_coder_muxer(AVOutputFormat **fmt, AVFormatContext **oc, const char *filename); 5 | 6 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/EncodingMuxingAudio.cpp: -------------------------------------------------------------------------------- 1 | #include "EncodingMuxingAudio.h" 2 | 3 | static AVFrame * 4 | alloc_audio_frame(enum AVSampleFormat sample_fmt, uint64_t channel_layout, int sample_rate, int nb_samples) { 5 | AVFrame *frame = av_frame_alloc(); 6 | int ret; 7 | 8 | if (!frame) { 9 | fprintf(stderr, "Error allocating an audio frame\n"); 10 | exit(1); 11 | } 12 | 13 | frame->format = sample_fmt; 14 | frame->channel_layout = channel_layout; 15 | frame->sample_rate = sample_rate; 16 | frame->nb_samples = nb_samples; 17 | 18 | if (nb_samples) { 19 | ret = av_frame_get_buffer(frame, 0); 20 | if (ret < 0) { 21 | fprintf(stderr, "Error allocating an audio buffer\n"); 22 | exit(1); 23 | } 24 | } 25 | 26 | return frame; 27 | } 28 | 29 | 30 | void Open_audio(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg) { 31 | AVCodecContext *c; 32 | int nb_samples; 33 | int ret; 34 | AVDictionary *opt = NULL; 35 | 36 | c = ost->st->codec; 37 | 38 | /* open it */ 39 | av_dict_copy(&opt, opt_arg, 0); 40 | ret = avcodec_open2(c, codec, &opt); 41 | av_dict_free(&opt); 42 | if (ret < 0) { 43 | fprintf(stderr, "Could not open audio codec: %d\n", ret); 44 | exit(1); 45 | } 46 | 47 | /* init signal generator */ 48 | ost->t = 0; 49 | ost->tincr = 2 * M_PI * 110.0 / c->sample_rate; 50 | /* increment frequency by 110 Hz per second */ 51 | ost->tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate; 52 | 53 | if (c->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) 54 | nb_samples = 10000; 55 | else 56 | nb_samples = c->frame_size; 57 | 58 | ost->frame = alloc_audio_frame(c->sample_fmt, c->channel_layout, c->sample_rate, nb_samples); 59 | ost->tmp_frame = alloc_audio_frame(AV_SAMPLE_FMT_S16, c->channel_layout, c->sample_rate, nb_samples); 60 | 61 | /* create resampler context */ 62 | ost->swr_ctx = swr_alloc(); 63 | if (!ost->swr_ctx) { 64 | fprintf(stderr, "Could not allocate resampler context\n"); 65 | exit(1); 66 | } 67 | 68 | /* set options */ 69 | av_opt_set_int(ost->swr_ctx, "in_channel_count", c->channels, 0); 70 | av_opt_set_int(ost->swr_ctx, "in_sample_rate", c->sample_rate, 0); 71 | av_opt_set_sample_fmt(ost->swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0); 72 | av_opt_set_int(ost->swr_ctx, "out_channel_count", c->channels, 0); 73 | av_opt_set_int(ost->swr_ctx, "out_sample_rate", c->sample_rate, 0); 74 | av_opt_set_sample_fmt(ost->swr_ctx, "out_sample_fmt", c->sample_fmt, 0); 75 | 76 | /* initialize the resampling context */ 77 | if ((ret = swr_init(ost->swr_ctx)) < 0) { 78 | fprintf(stderr, "Failed to initialize the resampling context\n"); 79 | exit(1); 80 | } 81 | } 82 | 83 | static int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt) { 84 | /* rescale output packet timestamp values from codec to stream timebase */ 85 | av_packet_rescale_ts(pkt, *time_base, st->time_base); 86 | pkt->stream_index = st->index; 87 | 88 | /* Write the compressed frame to the media file. */ 89 | // log_packet(fmt_ctx, pkt); 90 | return av_interleaved_write_frame(fmt_ctx, pkt); 91 | } 92 | 93 | /* Prepare a 16 bit dummy audio frame of 'frame_size' samples and 94 | * 'nb_channels' channels. */ 95 | static AVFrame *get_audio_frame(OutputStream *ost) { 96 | AVFrame *frame = ost->tmp_frame; 97 | int j, i, v; 98 | int16_t *q = (int16_t *) frame->data[0]; 99 | 100 | { 101 | AVRational r = {1, 1}; 102 | /* check if we want to generate more frames */ 103 | if (av_compare_ts(ost->next_pts, ost->st->codec->time_base, STREAM_DURATION, r) >= 0) 104 | return NULL; 105 | } 106 | 107 | for (j = 0; j < frame->nb_samples; j++) { 108 | v = (int) (sin(ost->t) * 10000); 109 | for (i = 0; i < ost->st->codec->channels; i++) 110 | *q++ = v; 111 | ost->t += ost->tincr; 112 | ost->tincr += ost->tincr2; 113 | } 114 | 115 | frame->pts = ost->next_pts; 116 | ost->next_pts += frame->nb_samples; 117 | 118 | return frame; 119 | } 120 | 121 | int Write_audio_frame(AVFormatContext *oc, OutputStream *ost) { 122 | AVCodecContext *c; 123 | AVPacket pkt = {0}; // data and size must be 0; 124 | AVFrame *frame; 125 | int ret; 126 | int got_packet; 127 | int dst_nb_samples; 128 | 129 | av_init_packet(&pkt); 130 | c = ost->st->codec; 131 | 132 | frame = get_audio_frame(ost); 133 | 134 | if (frame) { 135 | /* convert samples from native format to destination codec format, using the resampler */ 136 | /* compute destination number of samples */ 137 | dst_nb_samples = av_rescale_rnd(swr_get_delay(ost->swr_ctx, c->sample_rate) + frame->nb_samples, c->sample_rate, 138 | c->sample_rate, AV_ROUND_UP); 139 | av_assert0(dst_nb_samples == frame->nb_samples); 140 | 141 | /* when we pass a frame to the encoder, it may keep a reference to it 142 | * internally; 143 | * make sure we do not overwrite it here 144 | */ 145 | ret = av_frame_make_writable(ost->frame); 146 | if (ret < 0) 147 | exit(1); 148 | 149 | /* convert to destination format */ 150 | ret = swr_convert(ost->swr_ctx, 151 | ost->frame->data, dst_nb_samples, 152 | (const uint8_t **) frame->data, frame->nb_samples); 153 | if (ret < 0) { 154 | fprintf(stderr, "Error while converting\n"); 155 | exit(1); 156 | } 157 | frame = ost->frame; 158 | 159 | { 160 | AVRational r = {1, c->sample_rate}; 161 | frame->pts = av_rescale_q(ost->samples_count, r, c->time_base); 162 | } 163 | 164 | ost->samples_count += dst_nb_samples; 165 | } 166 | 167 | ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet); 168 | if (ret < 0) { 169 | fprintf(stderr, "Error encoding audio frame: %d\n", ret); 170 | exit(1); 171 | } 172 | 173 | if (got_packet) { 174 | ret = write_frame(oc, &c->time_base, ost->st, &pkt); 175 | if (ret < 0) { 176 | fprintf(stderr, "Error while writing audio frame: %d\n", ret); 177 | exit(1); 178 | } 179 | } 180 | 181 | return (frame || got_packet) ? 0 : 1; 182 | } 183 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/EncodingMuxingAudio.h: -------------------------------------------------------------------------------- 1 | #ifndef _ENCODING_MUXING_AUDIO_H_ 2 | #define _ENCODING_MUXING_AUDIO_H_ 3 | 4 | #include "common.h" 5 | #include "Stream.h" 6 | 7 | void Open_audio(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg); 8 | 9 | int Write_audio_frame(AVFormatContext *oc, OutputStream *ost); 10 | 11 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/EncodingMuxingHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef _ENCODING_MUXING_HEADER_H_ 2 | #define _ENCODING_MUXING_HEADER_H_ 3 | 4 | //#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__) 5 | 6 | extern "C" 7 | { 8 | #include "libavcodec/avcodec.h" 9 | #include "libavformat/avformat.h" 10 | #include "libavfilter/avfilter.h" 11 | #include "libavfilter/buffersink.h" 12 | #include "libavfilter/buffersrc.h" 13 | #include "libavutil/avutil.h" 14 | #include "libswscale/swscale.h" 15 | #include "libswresample/swresample.h" 16 | 17 | #include "libavutil/avassert.h" 18 | #include "libavutil/channel_layout.h" 19 | #include "libavutil/opt.h" 20 | #include "libavutil/mathematics.h" 21 | #include "libavutil/timestamp.h" 22 | }; 23 | 24 | /************************************************* 25 | Struct: IOParam 26 | Description: 接收命令行参数 27 | *************************************************/ 28 | typedef struct _IOParam { 29 | const char *input_file_name; //输入的像素文件名 30 | const char *output_file_name; //输出的封装视频文件名 31 | int frame_width; //视频帧宽度 32 | int frame_height; //视频帧高度 33 | } IOParam; 34 | 35 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/EncodingMuxingMain.cpp: -------------------------------------------------------------------------------- 1 | #include "Stream.h" 2 | #include "CoderMuxer.h" 3 | #include "EncodingMuxingVideo.h" 4 | #include "EncodingMuxingAudio.h" 5 | 6 | 7 | /************************************************* 8 | Function: hello 9 | Description: 解析命令行传入的参数 10 | Calls: 无 11 | Called By: main 12 | Input: (in)argc : 默认命令行参数 13 | (in)argv : 默认命令行参数 14 | Output: (out)io_param : 解析命令行的结果 15 | Return: true : 命令行解析正确 16 | false : 命令行解析错误 17 | *************************************************/ 18 | static bool hello(int argc, char **argv, AVDictionary *opt, IOParam &ioParam) { 19 | if (argc < 3) { 20 | printf("usage: %s output_file input_file frame_width frame_height\n" 21 | "API example program to output a media file with libavformat.\n" 22 | "This program generates a synthetic audio and video stream, encodes and\n" 23 | "muxes them into a file named output_file.\n" 24 | "The output format is automatically guessed according to the file extension.\n" 25 | "Raw images can also be output by using '%%d' in the filename.\n" 26 | "\n", argv[0]); 27 | return false; 28 | } 29 | 30 | ioParam.output_file_name = argv[1]; 31 | ioParam.input_file_name = argv[2]; 32 | ioParam.frame_width = atoi(argv[3]); 33 | ioParam.frame_height = atoi(argv[4]); 34 | 35 | return true; 36 | } 37 | 38 | /************************************************* 39 | Function: main 40 | Description: 入口点函数 41 | *************************************************/ 42 | int main(int argc, char **argv) { 43 | AVDictionary *opt = NULL; 44 | IOParam io = {NULL}; 45 | if (!hello(argc, argv, opt, io)) { 46 | return 1; 47 | } 48 | 49 | int ret; 50 | int have_video = 0, have_audio = 0; 51 | int encode_video = 0, encode_audio = 0; 52 | int videoFrameIdx = 0, audioFrameIdx = 0; 53 | 54 | OutputStream video_st = {0}, audio_st = {0}; 55 | AVOutputFormat *fmt; 56 | AVFormatContext *oc; 57 | AVCodec *audio_codec = NULL, *video_codec = NULL; 58 | 59 | Open_coder_muxer(&fmt, &oc, io.output_file_name); 60 | 61 | /* Add the audio and video streams using the default format codecs 62 | * and initialize the codecs. */ 63 | ret = Add_audio_video_streams(&video_st, &audio_st, oc, fmt, audio_codec, video_codec, io); 64 | have_video = ret & HAVE_VIDEO; 65 | encode_video = ret & ENCODE_VIDEO; 66 | have_audio = ret & HAVE_AUDIO; 67 | encode_audio = ret & ENCODE_AUDIO; 68 | 69 | /* Now that all the parameters are set, we can open the audio and 70 | * video codecs and allocate the necessary encode buffers. */ 71 | if (have_video) { 72 | Open_video(oc, video_codec, &video_st, opt, io); 73 | } 74 | if (have_audio) { 75 | Open_audio(oc, audio_codec, &audio_st, opt); 76 | } 77 | 78 | av_dump_format(oc, 0, io.output_file_name, 1); 79 | /* open the output file, if needed */ 80 | if (!(fmt->flags & AVFMT_NOFILE)) { 81 | ret = avio_open(&oc->pb, io.output_file_name, AVIO_FLAG_WRITE); 82 | if (ret < 0) { 83 | fprintf(stderr, "Could not open '%s': %d\n", io.output_file_name, ret); 84 | return 1; 85 | } 86 | } 87 | 88 | /* Write the stream header, if any. */ 89 | ret = avformat_write_header(oc, &opt); 90 | if (ret < 0) { 91 | fprintf(stderr, "Error occurred when opening output file: %d\n", ret); 92 | return 1; 93 | } 94 | 95 | while (encode_video || encode_audio) { 96 | /* select the stream to encode */ 97 | if (encode_video && (!encode_audio || 98 | av_compare_ts(video_st.next_pts, video_st.st->codec->time_base, audio_st.next_pts, 99 | audio_st.st->codec->time_base) <= 0)) { 100 | encode_video = !Write_video_frame(oc, &video_st); 101 | if (encode_video) { 102 | printf("Write %d video frame.\n", videoFrameIdx++); 103 | } else { 104 | printf("Video ended, exit.\n"); 105 | } 106 | } else { 107 | encode_audio = !Write_audio_frame(oc, &audio_st); 108 | if (encode_audio) { 109 | printf("Write %d audio frame.\n", audioFrameIdx++); 110 | } else { 111 | printf("Audio ended, exit.\n"); 112 | } 113 | } 114 | } 115 | 116 | //写入文件尾数据 117 | av_write_trailer(oc); 118 | 119 | /* Close each codec. */ 120 | if (have_video) { 121 | Close_stream(oc, &video_st); 122 | } 123 | if (have_audio) { 124 | Close_stream(oc, &audio_st); 125 | } 126 | 127 | if (!(fmt->flags & AVFMT_NOFILE)) { 128 | //关闭输出文件 129 | avio_closep(&oc->pb); 130 | } 131 | 132 | //关闭输出文件的上下文句柄 133 | avformat_free_context(oc); 134 | 135 | printf("Procssing succeeded.\n"); 136 | return 0; 137 | } -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/EncodingMuxingVideo.cpp: -------------------------------------------------------------------------------- 1 | #include "EncodingMuxingVideo.h" 2 | 3 | 4 | FILE *g_inputYUVFile = NULL; 5 | 6 | static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height) { 7 | AVFrame *picture; 8 | int ret; 9 | 10 | picture = av_frame_alloc(); 11 | if (!picture) { 12 | return NULL; 13 | } 14 | 15 | picture->format = pix_fmt; 16 | picture->width = width; 17 | picture->height = height; 18 | 19 | /* allocate the buffers for the frame data */ 20 | ret = av_frame_get_buffer(picture, 32); 21 | if (ret < 0) { 22 | fprintf(stderr, "Could not allocate frame data.\n"); 23 | exit(1); 24 | } 25 | 26 | return picture; 27 | } 28 | 29 | void Open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg, IOParam &io) { 30 | int ret; 31 | AVCodecContext *c = ost->st->codec; 32 | AVDictionary *opt = NULL; 33 | 34 | av_dict_copy(&opt, opt_arg, 0); 35 | 36 | /* open the codec */ 37 | ret = avcodec_open2(c, codec, &opt); 38 | av_dict_free(&opt); 39 | if (ret < 0) { 40 | fprintf(stderr, "Could not open video codec: %d\n", ret); 41 | exit(1); 42 | } 43 | 44 | /* allocate and init a re-usable frame */ 45 | ost->frame = alloc_picture(c->pix_fmt, c->width, c->height); 46 | if (!ost->frame) { 47 | fprintf(stderr, "Could not allocate video frame\n"); 48 | exit(1); 49 | } 50 | 51 | //打开输入YUV文件 52 | g_inputYUVFile = fopen(io.input_file_name, "rb+"); 53 | if (g_inputYUVFile == NULL) { 54 | fprintf(stderr, "Open input yuv file failed.\n"); 55 | exit(1); 56 | } 57 | } 58 | 59 | 60 | static int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt) { 61 | /* rescale output packet timestamp values from codec to stream timebase */ 62 | av_packet_rescale_ts(pkt, *time_base, st->time_base); 63 | pkt->stream_index = st->index; 64 | 65 | /* Write the compressed frame to the media file. */ 66 | // log_packet(fmt_ctx, pkt); 67 | return av_interleaved_write_frame(fmt_ctx, pkt); 68 | } 69 | 70 | static void fill_yuv_image(AVFrame *pict, int frame_index, int width, int height) { 71 | int x, y, i, ret; 72 | 73 | /* when we pass a frame to the encoder, it may keep a reference to it 74 | * internally; 75 | * make sure we do not overwrite it here 76 | */ 77 | ret = av_frame_make_writable(pict); 78 | if (ret < 0) { 79 | exit(1); 80 | } 81 | 82 | i = frame_index; 83 | 84 | /* Y */ 85 | for (y = 0; y < height; y++) { 86 | ret = fread(&pict->data[0][y * pict->linesize[0]], 1, width, g_inputYUVFile); 87 | if (ret != width) { 88 | printf("Error: Read Y data error.\n"); 89 | exit(1); 90 | } 91 | } 92 | 93 | /* U */ 94 | for (y = 0; y < height / 2; y++) { 95 | ret = fread(&pict->data[1][y * pict->linesize[1]], 1, width / 2, g_inputYUVFile); 96 | if (ret != width / 2) { 97 | printf("Error: Read U data error.\n"); 98 | exit(1); 99 | } 100 | } 101 | 102 | /* V */ 103 | for (y = 0; y < height / 2; y++) { 104 | ret = fread(&pict->data[2][y * pict->linesize[2]], 1, width / 2, g_inputYUVFile); 105 | if (ret != width / 2) { 106 | printf("Error: Read V data error.\n"); 107 | exit(1); 108 | } 109 | } 110 | } 111 | 112 | 113 | static AVFrame *get_video_frame(OutputStream *ost) { 114 | AVCodecContext *c = ost->st->codec; 115 | 116 | /* check if we want to generate more frames */ 117 | { 118 | AVRational r = {1, 1}; 119 | if (av_compare_ts(ost->next_pts, ost->st->codec->time_base, STREAM_DURATION, r) >= 0) { 120 | return NULL; 121 | } 122 | } 123 | 124 | fill_yuv_image(ost->frame, ost->next_pts, c->width, c->height); 125 | 126 | ost->frame->pts = ost->next_pts++; 127 | 128 | return ost->frame; 129 | } 130 | 131 | int Write_video_frame(AVFormatContext *oc, OutputStream *ost) { 132 | int ret; 133 | AVCodecContext *c; 134 | AVFrame *frame; 135 | int got_packet = 0; 136 | AVPacket pkt = {0}; 137 | 138 | c = ost->st->codec; 139 | 140 | frame = get_video_frame(ost); 141 | 142 | av_init_packet(&pkt); 143 | 144 | /* encode the image */ 145 | ret = avcodec_encode_video2(c, &pkt, frame, &got_packet); 146 | if (ret < 0) { 147 | fprintf(stderr, "Error encoding video frame: %d\n", ret); 148 | exit(1); 149 | } 150 | 151 | if (got_packet) { 152 | ret = write_frame(oc, &c->time_base, ost->st, &pkt); 153 | } else { 154 | ret = 0; 155 | } 156 | 157 | if (ret < 0) { 158 | fprintf(stderr, "Error while writing video frame: %d\n", ret); 159 | exit(1); 160 | } 161 | 162 | return (frame || got_packet) ? 0 : 1; 163 | } 164 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/EncodingMuxingVideo.h: -------------------------------------------------------------------------------- 1 | #ifndef _ENCODING_MUXING_VIDEO_H_ 2 | #define _ENCODING_MUXING_VIDEO_H_ 3 | 4 | #include "common.h" 5 | #include "Stream.h" 6 | 7 | typedef struct _IOParam IOParam; 8 | 9 | void Open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg, IOParam &io); 10 | 11 | int Write_video_frame(AVFormatContext *oc, OutputStream *ost); 12 | 13 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/Stream.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "Stream.h" 3 | 4 | 5 | /* Add an output stream. */ 6 | static void add_stream(OutputStream *ost, AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id) { 7 | AVCodecContext *c; 8 | int i; 9 | 10 | /* find the encoder */ 11 | *codec = avcodec_find_encoder(codec_id); 12 | if (!(*codec)) { 13 | fprintf(stderr, "Could not find encoder for '%s'\n", 14 | avcodec_get_name(codec_id)); 15 | exit(1); 16 | } 17 | 18 | ost->st = avformat_new_stream(oc, *codec); 19 | if (!ost->st) { 20 | fprintf(stderr, "Could not allocate stream\n"); 21 | exit(1); 22 | } 23 | ost->st->id = oc->nb_streams - 1; 24 | c = ost->st->codec; 25 | 26 | switch ((*codec)->type) { 27 | case AVMEDIA_TYPE_AUDIO: 28 | c->sample_fmt = (*codec)->sample_fmts ? (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP; 29 | c->bit_rate = 64000; 30 | c->sample_rate = 44100; 31 | 32 | if ((*codec)->supported_samplerates) { 33 | c->sample_rate = (*codec)->supported_samplerates[0]; 34 | for (i = 0; (*codec)->supported_samplerates[i]; i++) { 35 | if ((*codec)->supported_samplerates[i] == 44100) 36 | c->sample_rate = 44100; 37 | } 38 | } 39 | 40 | c->channels = av_get_channel_layout_nb_channels(c->channel_layout); 41 | c->channel_layout = AV_CH_LAYOUT_STEREO; 42 | if ((*codec)->channel_layouts) { 43 | c->channel_layout = (*codec)->channel_layouts[0]; 44 | for (i = 0; (*codec)->channel_layouts[i]; i++) { 45 | if ((*codec)->channel_layouts[i] == AV_CH_LAYOUT_STEREO) 46 | c->channel_layout = AV_CH_LAYOUT_STEREO; 47 | } 48 | } 49 | c->channels = av_get_channel_layout_nb_channels(c->channel_layout); 50 | { 51 | AVRational r = {1, c->sample_rate}; 52 | ost->st->time_base = r; 53 | } 54 | break; 55 | 56 | case AVMEDIA_TYPE_VIDEO: 57 | c->codec_id = codec_id; 58 | 59 | c->bit_rate = 400000; 60 | /* Resolution must be a multiple of two. */ 61 | c->width = 352; 62 | c->height = 288; 63 | /* timebase: This is the fundamental unit of time (in seconds) in terms 64 | * of which frame timestamps are represented. For fixed-fps content, 65 | * timebase should be 1/framerate and timestamp increments should be 66 | * identical to 1. */ 67 | { 68 | AVRational r = {1, STREAM_FRAME_RATE}; 69 | ost->st->time_base = r; 70 | } 71 | c->time_base = ost->st->time_base; 72 | 73 | c->gop_size = 12; /* emit one intra frame every twelve frames at most */ 74 | c->pix_fmt = AV_PIX_FMT_YUV420P; 75 | if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) { 76 | /* just for testing, we also add B frames */ 77 | c->max_b_frames = 2; 78 | } 79 | if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) { 80 | /* Needed to avoid using macroblocks in which some coeffs overflow. 81 | * This does not happen with normal video, it just happens here as 82 | * the motion of the chroma plane does not match the luma plane. */ 83 | c->mb_decision = 2; 84 | } 85 | break; 86 | 87 | default: 88 | break; 89 | } 90 | 91 | /* Some formats want stream headers to be separate. */ 92 | if (oc->oformat->flags & AVFMT_GLOBALHEADER) 93 | c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; 94 | } 95 | 96 | 97 | int Add_audio_video_streams(OutputStream *video_st, OutputStream *audio_st, AVFormatContext *oc, AVOutputFormat *fmt, 98 | AVCodec *audio_codec, AVCodec *video_codec, IOParam &io) { 99 | int ret = 0; 100 | if (fmt->video_codec != AV_CODEC_ID_NONE) { 101 | add_stream(video_st, oc, &video_codec, fmt->video_codec); 102 | video_st->st->codec->width = io.frame_width; 103 | video_st->st->codec->height = io.frame_height; 104 | ret |= HAVE_VIDEO; 105 | ret |= ENCODE_VIDEO; 106 | } 107 | if (fmt->audio_codec != AV_CODEC_ID_NONE) { 108 | add_stream(audio_st, oc, &audio_codec, fmt->audio_codec); 109 | ret |= HAVE_AUDIO; 110 | ret |= ENCODE_AUDIO; 111 | } 112 | 113 | return ret; 114 | } 115 | 116 | void Close_stream(AVFormatContext *oc, OutputStream *ost) { 117 | avcodec_close(ost->st->codec); 118 | av_frame_free(&ost->frame); 119 | av_frame_free(&ost->tmp_frame); 120 | sws_freeContext(ost->sws_ctx); 121 | swr_free(&ost->swr_ctx); 122 | } 123 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/Stream.h: -------------------------------------------------------------------------------- 1 | #ifndef _STREAM_H_ 2 | #define _STREAM_H_ 3 | 4 | #include "common.h" 5 | 6 | #define HAVE_VIDEO 1 //目标文件包含视频 7 | #define ENCODE_VIDEO 1 << 1 //编码视频数据 8 | #define HAVE_AUDIO 1 << 2 //目标文件包含音频 9 | #define ENCODE_AUDIO 1 << 3 //编码音频数据 10 | 11 | typedef struct OutputStream { 12 | AVStream *st; 13 | 14 | /* pts of the next frame that will be generated */ 15 | int64_t next_pts; 16 | int samples_count; 17 | 18 | AVFrame *frame; 19 | AVFrame *tmp_frame; 20 | 21 | float t, tincr, tincr2; 22 | 23 | struct SwsContext *sws_ctx; 24 | struct SwrContext *swr_ctx; 25 | } OutputStream; 26 | 27 | int Add_audio_video_streams(OutputStream *video_st, OutputStream *audio_st, AVFormatContext *oc, AVOutputFormat *fmt, 28 | AVCodec *audio_codec, AVCodec *video_codec, IOParam &io); 29 | 30 | void Close_stream(AVFormatContext *oc, OutputStream *ost); 31 | 32 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoEncodingMuxing/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_H_ 2 | #define _COMMON_H_ 3 | 4 | #include "EncodingMuxingHeader.h" 5 | 6 | #define STREAM_DURATION 10.0 7 | #define STREAM_FRAME_RATE 25 /* 25 images/s */ 8 | #define SCALE_FLAGS SWS_BICUBIC 9 | 10 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoFiltering/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(.) 2 | add_executable(VideoFilteringMain VideoFilteringMain.cpp Filter.cpp Frame.cpp IOFiles.cpp) 3 | target_link_libraries(VideoFilteringMain ${LINK_LIBS} ${FFMPEG_LIBS}) -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoFiltering/Filter.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | AVFilterContext *buffersink_ctx; 4 | AVFilterContext *buffersrc_ctx; 5 | AVFilterGraph *filter_graph; 6 | 7 | //初始化video filter相关的结构 8 | int Init_video_filter(const char *filter_descr, int width, int height) { 9 | char args[512]; 10 | const AVFilter *buffersrc = avfilter_get_by_name("buffer"); 11 | const AVFilter *buffersink = avfilter_get_by_name("buffersink"); 12 | AVFilterInOut *outputs = avfilter_inout_alloc(); 13 | AVFilterInOut *inputs = avfilter_inout_alloc(); 14 | enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE}; 15 | AVBufferSinkParams *buffersink_params; 16 | 17 | filter_graph = avfilter_graph_alloc(); 18 | 19 | /* buffer video source: the decoded frames from the decoder will be inserted here. */ 20 | snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", width, height, 21 | AV_PIX_FMT_YUV420P, 1, 25, 1, 1); 22 | int ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, filter_graph); 23 | if (ret < 0) { 24 | printf("Error: cannot create buffer source.\n"); 25 | return ret; 26 | } 27 | 28 | /* buffer video sink: to terminate the filter chain. */ 29 | buffersink_params = av_buffersink_params_alloc(); 30 | buffersink_params->pixel_fmts = pix_fmts; 31 | ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, buffersink_params, filter_graph); 32 | av_free(buffersink_params); 33 | if (ret < 0) { 34 | printf("Error: cannot create buffer sink\n"); 35 | return ret; 36 | } 37 | 38 | /* Endpoints for the filter graph. */ 39 | outputs->name = av_strdup("in"); 40 | outputs->filter_ctx = buffersrc_ctx; 41 | outputs->pad_idx = 0; 42 | outputs->next = NULL; 43 | 44 | inputs->name = av_strdup("out"); 45 | inputs->filter_ctx = buffersink_ctx; 46 | inputs->pad_idx = 0; 47 | inputs->next = NULL; 48 | 49 | if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_descr, &inputs, &outputs, NULL)) < 0) { 50 | printf("Error: avfilter_graph_parse_ptr failed.\n"); 51 | return ret; 52 | } 53 | 54 | if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) { 55 | printf("Error: avfilter_graph_config"); 56 | return ret; 57 | } 58 | 59 | return 0; 60 | } 61 | 62 | //将待处理的输入frame添加进filter graph 63 | bool Add_frame_to_filter(AVFrame *frameIn) { 64 | if (av_buffersrc_add_frame(buffersrc_ctx, frameIn) < 0) { 65 | return false; 66 | } 67 | 68 | return true; 69 | } 70 | 71 | //从filter graph中获取输出frame 72 | int Get_frame_from_filter(AVFrame **frameOut) { 73 | if (av_buffersink_get_frame(buffersink_ctx, *frameOut) < 0) { 74 | return false; 75 | } 76 | 77 | return true; 78 | } 79 | 80 | //关闭video filter相关结构 81 | void Close_video_filter() { 82 | avfilter_graph_free(&filter_graph); 83 | } -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoFiltering/Filter.h: -------------------------------------------------------------------------------- 1 | #ifndef _FILTER_H_ 2 | #define _FILTER_H_ 3 | 4 | /************************************************* 5 | Function: Init_video_filter 6 | Description: 初始化video filter相关的结构 7 | Calls: 无 8 | Called By: main 9 | Input: (in)filter_descr : 指定的filter描述 10 | (in)width : 输入图像宽度 11 | (in)height : 输入图像高度 12 | Output: 无 13 | Return: 0 : video filter初始化正确 14 | < 0 : video filter初始化正确 15 | *************************************************/ 16 | int Init_video_filter(const char *filter_descr, int width, int height); 17 | 18 | /************************************************* 19 | Function: Add_frame_to_filter 20 | Description: 将待处理的输入frame添加进filter graph 21 | Calls: 无 22 | Called By: main 23 | Input: (in)frameIn : 待处理的输入frame 24 | Output: 无 25 | Return: true : 添加输入frame正确 26 | false : 添加输入frame正确 27 | *************************************************/ 28 | bool Add_frame_to_filter(AVFrame *frameIn); 29 | 30 | /************************************************* 31 | Function: Get_frame_from_filter 32 | Description: 从filter graph中获取输出frame 33 | Calls: 无 34 | Called By: main 35 | Input: 无 36 | Output: (in)frameOut : 保存输出像素的frame 37 | Return: 0 : 添加输入frame正确 38 | < 0 : 添加输入frame正确 39 | *************************************************/ 40 | int Get_frame_from_filter(AVFrame **frameOut); 41 | 42 | /************************************************* 43 | Function: Close_video_filter 44 | Description: 关闭video filter相关结构 45 | Calls: 无 46 | Called By: main 47 | Input: 无 48 | Output: 无 49 | Return: 无 50 | *************************************************/ 51 | void Close_video_filter(); 52 | 53 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoFiltering/Frame.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | //初始化输入输出frame对象和像素缓存 4 | void Init_video_frame_in_out(AVFrame **frameIn, AVFrame **frameOut, unsigned char **frame_buffer_in, 5 | unsigned char **frame_buffer_out, int frameWidth, int frameHeight) { 6 | *frameIn = av_frame_alloc(); 7 | *frame_buffer_in = (unsigned char *) av_malloc( 8 | av_image_get_buffer_size(AV_PIX_FMT_YUV420P, frameWidth, frameHeight, 1)); 9 | av_image_fill_arrays((*frameIn)->data, (*frameIn)->linesize, *frame_buffer_in, AV_PIX_FMT_YUV420P, frameWidth, 10 | frameHeight, 1); 11 | 12 | *frameOut = av_frame_alloc(); 13 | *frame_buffer_out = (unsigned char *) av_malloc( 14 | av_image_get_buffer_size(AV_PIX_FMT_YUV420P, frameWidth, frameHeight, 1)); 15 | av_image_fill_arrays((*frameOut)->data, (*frameOut)->linesize, *frame_buffer_out, AV_PIX_FMT_YUV420P, frameWidth, 16 | frameHeight, 1); 17 | 18 | (*frameIn)->width = frameWidth; 19 | (*frameIn)->height = frameHeight; 20 | (*frameIn)->format = AV_PIX_FMT_YUV420P; 21 | } 22 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoFiltering/Frame.h: -------------------------------------------------------------------------------- 1 | #ifndef _FRAME_H_ 2 | #define _FRAME_H_ 3 | 4 | /************************************************* 5 | Function: Init_video_frame_in_out 6 | Description: 初始化输入输出frame对象和像素缓存 7 | Calls: 无 8 | Called By: main 9 | Input: (in)frameWidth : 图像宽度 10 | (in)frameHeight : 图像高度 11 | Output: (out)frameIn : 输入frame 12 | (out)frameOut : 输出frame 13 | (out)frame_buffer_in :输入frame的像素缓存 14 | (out)frame_buffer_out:输出frame的像素缓存 15 | Return: 无 16 | *************************************************/ 17 | void Init_video_frame_in_out(AVFrame **frameIn, AVFrame **frameOut, unsigned char **frame_buffer_in, 18 | unsigned char **frame_buffer_out, int frameWidth, int frameHeight); 19 | 20 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoFiltering/IOFiles.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | //从输入yuv文件中读取数据到buffer和frame结构 4 | bool Read_yuv_data_to_buf(unsigned char *frame_buffer_in, const IOFiles &files, AVFrame **frameIn) 5 | { 6 | AVFrame *pFrameIn = *frameIn; 7 | int width = files.frameWidth, height = files.frameHeight; 8 | int frameSize = width * height * 3 / 2; 9 | 10 | if (fread(frame_buffer_in, 1, frameSize, files.iFile) != frameSize) 11 | { 12 | return false; 13 | } 14 | 15 | pFrameIn->data[0] = frame_buffer_in; 16 | pFrameIn->data[1] = pFrameIn->data[0] + width * height; 17 | pFrameIn->data[2] = pFrameIn->data[1] + width * height / 4; 18 | 19 | return true; 20 | } 21 | 22 | //从输出frame中写出像素数据到输出文件 23 | void Write_yuv_to_outfile(const AVFrame *frame_out, IOFiles &files) 24 | { 25 | if(frame_out->format==AV_PIX_FMT_YUV420P) 26 | { 27 | for(int i=0;iheight;i++) 28 | { 29 | fwrite(frame_out->data[0]+frame_out->linesize[0]*i,1,frame_out->width,files.oFile); 30 | } 31 | for(int i=0;iheight/2;i++) 32 | { 33 | fwrite(frame_out->data[1]+frame_out->linesize[1]*i,1,frame_out->width/2,files.oFile); 34 | } 35 | for(int i=0;iheight/2;i++) 36 | { 37 | fwrite(frame_out->data[2]+frame_out->linesize[2]*i,1,frame_out->width/2,files.oFile); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoFiltering/IOFiles.h: -------------------------------------------------------------------------------- 1 | #ifndef _IO_FILES_H_ 2 | #define _IO_FILES_H_ 3 | 4 | /************************************************* 5 | Struct: IOParam 6 | Description: 接收命令行参数 7 | *************************************************/ 8 | typedef struct _IOFiles { 9 | const char *inputFileName; //输入文件名 10 | const char *outputFileName; //输出文件名 11 | 12 | FILE *iFile; //输入文件指针 13 | FILE *oFile; //输出文件指针 14 | 15 | uint8_t filterIdx; //Filter索引 16 | 17 | unsigned int frameWidth; //图像宽度 18 | unsigned int frameHeight; //图像高度 19 | } IOFiles; 20 | 21 | /************************************************* 22 | Function: Read_yuv_data_to_buf 23 | Description: 从输入yuv文件中读取数据到buffer和frame结构 24 | Calls: 无 25 | Called By: main 26 | Input: (in)files : 包含输入文件的结构 27 | Output: (out)frame_buffer_in : 指向缓存区的指针 28 | (out)frameIn : 指向输入frame的指针 29 | Return: 无 30 | *************************************************/ 31 | bool Read_yuv_data_to_buf(unsigned char *frame_buffer_in, const IOFiles &files, AVFrame **frameIn); 32 | 33 | /************************************************* 34 | Function: Write_yuv_to_outfile 35 | Description: 从输出frame中写出像素数据到输出文件 36 | Calls: 无 37 | Called By: main 38 | Input: (in)frame_out : filter graph输出的frame 39 | Output: (out)files : 包含输出文件的结构 40 | Return: 无 41 | *************************************************/ 42 | void Write_yuv_to_outfile(const AVFrame *frame_out, IOFiles &files); 43 | 44 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoFiltering/VideoFilteringHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef _VIDEO_FILTERING_HEADER_H_ 2 | #define _VIDEO_FILTERING_HEADER_H_ 3 | 4 | //#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__) 5 | 6 | extern "C" 7 | { 8 | #include "libavfilter/avfilter.h" 9 | #include "libavfilter/buffersink.h" 10 | #include "libavfilter/buffersrc.h" 11 | #include "libavutil/avutil.h" 12 | #include "libavutil/imgutils.h" 13 | }; 14 | 15 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoFiltering/VideoFilteringMain.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | /************************************************* 4 | Function: hello 5 | Description: 解析命令行传入的参数 6 | Calls: 无 7 | Called By: main 8 | Input: (in)argc : 默认命令行参数 9 | (in)argv : 默认命令行参数 10 | Output: (out)io_param : 解析命令行的结果 11 | Return: true : 命令行解析正确 12 | false : 命令行解析错误 13 | *************************************************/ 14 | static int hello(int argc, char **argv, IOFiles &files) { 15 | if (argc < 4) { 16 | printf("usage: %s output_file input_file filter_index\n" 17 | "Filter index:.\n" 18 | "1. Color component\n" 19 | "2. Blur\n" 20 | "3. Horizonal flip\n" 21 | "4. HUE\n" 22 | "5. Crop\n" 23 | "6. Box\n" 24 | "7. Text\n" 25 | "\n", argv[0]); 26 | 27 | return -1; 28 | } 29 | 30 | files.inputFileName = argv[1]; 31 | files.outputFileName = argv[2]; 32 | files.frameWidth = atoi(argv[3]); 33 | files.frameHeight = atoi(argv[4]); 34 | files.filterIdx = atoi(argv[5]); 35 | 36 | files.iFile = fopen(files.inputFileName, "rb+"); 37 | if (!files.iFile) { 38 | printf("Error: open input file failed.\n"); 39 | return -1; 40 | } 41 | 42 | files.oFile = fopen(files.outputFileName, "wb+"); 43 | if (!files.oFile) { 44 | printf("Error: open output file failed.\n"); 45 | return -1; 46 | } 47 | 48 | return 0; 49 | } 50 | 51 | /************************************************* 52 | Function: setFilterDescr 53 | Description: 根据索引设置Filter描述符 54 | Calls: 无 55 | Called By: main 56 | Input: (in)idx : Filter的索引 57 | Output: (out)filter_descr : Filter描述符 58 | Return: 无 59 | *************************************************/ 60 | static void setFilterDescr(int idx, char **filter_descr) { 61 | switch (idx) { 62 | case 1: 63 | *filter_descr = "lutyuv='u=128:v=128'"; 64 | case 2: 65 | *filter_descr = "boxblur"; 66 | case 3: 67 | *filter_descr = "hflip"; 68 | case 4: 69 | *filter_descr = "hue='h=60:s=-3'"; 70 | case 5: 71 | *filter_descr = "crop=2/3*in_w:2/3*in_h"; 72 | case 6: 73 | *filter_descr = "drawbox=x=100:y=100:w=100:h=100:color=pink@0.5"; 74 | case 7: 75 | *filter_descr = "drawtext=fontfile=arial.ttf:fontcolor=green:fontsize=30:text='FFMpeg Filter Demo'"; 76 | } 77 | } 78 | 79 | /************************************************* 80 | Function: main 81 | Description: 入口点函数 82 | *************************************************/ 83 | int main(int argc, char **argv) { 84 | int ret = 0; 85 | AVFrame *frame_in = NULL; 86 | AVFrame *frame_out = NULL; 87 | unsigned char *frame_buffer_in = NULL; 88 | unsigned char *frame_buffer_out = NULL; 89 | char *filter_descr = NULL; 90 | 91 | IOFiles files = {NULL}; 92 | 93 | //命令行解析 94 | if (hello(argc, argv, files) < 0) { 95 | //关闭文件及相关结构 96 | fclose(files.iFile); 97 | fclose(files.oFile); 98 | 99 | av_frame_free(&frame_in); 100 | av_frame_free(&frame_out); 101 | 102 | Close_video_filter(); 103 | return 0; 104 | } 105 | 106 | int frameWidth = files.frameWidth, frameHeight = files.frameHeight; 107 | 108 | //设置Filter的内容 109 | setFilterDescr(files.filterIdx, &filter_descr); 110 | 111 | avfilter_register_all(); 112 | 113 | //初始化Filter相关结构 114 | if (ret = Init_video_filter(filter_descr, frameWidth, frameHeight)) { 115 | goto end; 116 | } 117 | 118 | //分配输入和输出frame和其存储空间 119 | Init_video_frame_in_out(&frame_in, &frame_out, &frame_buffer_in, &frame_buffer_out, frameWidth, frameHeight); 120 | 121 | while (Read_yuv_data_to_buf(frame_buffer_in, files, &frame_in)) { 122 | //将输入frame添加到filter graph 123 | if (!Add_frame_to_filter(frame_in)) { 124 | printf("Error while adding frame.\n"); 125 | goto end; 126 | } 127 | 128 | //从filter graph中获取输出frame 129 | if (!Get_frame_from_filter(&frame_out)) { 130 | printf("Error while getting frame.\n"); 131 | goto end; 132 | } 133 | 134 | //将输出frame写出到输出文件 135 | Write_yuv_to_outfile(frame_out, files); 136 | 137 | printf("Process 1 frame!\n"); 138 | av_frame_unref(frame_out); 139 | } 140 | 141 | end: 142 | 143 | //关闭文件及相关结构 144 | fclose(files.iFile); 145 | fclose(files.oFile); 146 | 147 | av_frame_free(&frame_in); 148 | av_frame_free(&frame_out); 149 | 150 | Close_video_filter(); 151 | 152 | return 0; 153 | } -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoFiltering/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_H_ 2 | #define _COMMON_H_ 3 | 4 | #include 5 | #include "VideoFilteringHeader.h" 6 | #include "IOFiles.h" 7 | #include "Filter.h" 8 | #include "Frame.h" 9 | 10 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoRemuxing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(.) 2 | add_executable(VideoRemuxingMain VideoRemuxingMain.cpp) 3 | target_link_libraries(VideoRemuxingMain ${LINK_LIBS} ${FFMPEG_LIBS}) -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoRemuxing/IOFiles.h: -------------------------------------------------------------------------------- 1 | #ifndef _IO_FILES_H_ 2 | #define _IO_FILES_H_ 3 | 4 | typedef struct _IOFiles { 5 | const char *inputName; 6 | const char *outputName; 7 | } IOFiles; 8 | 9 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoRemuxing/VideoRemuxingHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef _VIDEO_REMUXING_HEADER_H_ 2 | #define _VIDEO_REMUXING_HEADER_H_ 3 | 4 | //#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__) 5 | extern "C" 6 | { 7 | #include "libavutil/timestamp.h" 8 | #include "libavformat/avformat.h" 9 | }; 10 | 11 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoRemuxing/VideoRemuxingMain.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | /************************************************* 4 | Function: hello 5 | Description: 解析命令行传入的参数 6 | Calls: 无 7 | Called By: main 8 | Input: (in)argc : 默认命令行参数 9 | (in)argv : 默认命令行参数 10 | Output: (out)io_param : 解析命令行的结果 11 | Return: true : 命令行解析正确 12 | false : 命令行解析错误 13 | *************************************************/ 14 | static bool hello(int argc, char **argv, IOFiles &io_param) { 15 | printf("FFMpeg Remuxing Demo.\nCommand format: %s inputfile outputfile\n", argv[0]); 16 | if (argc != 3) { 17 | printf("Error: command line error, please re-check.\n"); 18 | return false; 19 | } 20 | 21 | io_param.inputName = argv[1]; 22 | io_param.outputName = argv[2]; 23 | 24 | return true; 25 | } 26 | 27 | 28 | /************************************************* 29 | Function: main 30 | Description: 入口点函数 31 | *************************************************/ 32 | int main(int argc, char **argv) { 33 | IOFiles io_param; 34 | if (!hello(argc, argv, io_param)) { 35 | return -1; 36 | } 37 | 38 | AVOutputFormat *ofmt = NULL; 39 | AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL; 40 | AVPacket pkt; 41 | int ret = 0; 42 | 43 | av_register_all(); 44 | 45 | //按封装格式打开输入视频文件 46 | if ((ret = avformat_open_input(&ifmt_ctx, io_param.inputName, NULL, NULL)) < 0) { 47 | printf("Error: Open input file failed.\n"); 48 | goto end; 49 | } 50 | 51 | //获取输入视频文件中的流信息 52 | if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) { 53 | printf("Error: Failed to retrieve input stream information.\n"); 54 | goto end; 55 | } 56 | av_dump_format(ifmt_ctx, 0, io_param.inputName, 0); 57 | 58 | //按照文件名获取输出文件的句柄 59 | avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, io_param.outputName); 60 | if (!ofmt_ctx) { 61 | printf("Error: Could not create output context.\n"); 62 | goto end; 63 | } 64 | ofmt = ofmt_ctx->oformat; 65 | 66 | for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) { 67 | AVStream *inStream = ifmt_ctx->streams[i]; 68 | AVStream *outStream = avformat_new_stream(ofmt_ctx, inStream->codec->codec); 69 | if (!outStream) { 70 | printf("Error: Could not allocate output stream.\n"); 71 | goto end; 72 | } 73 | 74 | ret = avcodec_copy_context(outStream->codec, inStream->codec); 75 | outStream->codec->codec_tag = 0; 76 | if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) { 77 | outStream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; 78 | } 79 | } 80 | 81 | av_dump_format(ofmt_ctx, 0, io_param.outputName, 1); 82 | 83 | if (!(ofmt->flags & AVFMT_NOFILE)) { 84 | ret = avio_open(&ofmt_ctx->pb, io_param.outputName, AVIO_FLAG_WRITE); 85 | if (ret < 0) { 86 | printf("Error: Could not open output file.\n"); 87 | goto end; 88 | } 89 | } 90 | 91 | ret = avformat_write_header(ofmt_ctx, NULL); 92 | if (ret < 0) { 93 | printf("Error: Could not write output file header.\n"); 94 | goto end; 95 | } 96 | 97 | while (1) { 98 | AVStream *in_stream, *out_stream; 99 | 100 | ret = av_read_frame(ifmt_ctx, &pkt); 101 | if (ret < 0) 102 | break; 103 | 104 | in_stream = ifmt_ctx->streams[pkt.stream_index]; 105 | out_stream = ofmt_ctx->streams[pkt.stream_index]; 106 | 107 | /* copy packet */ 108 | pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, 109 | (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); 110 | pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, 111 | (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); 112 | pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base); 113 | pkt.pos = -1; 114 | 115 | ret = av_interleaved_write_frame(ofmt_ctx, &pkt); 116 | if (ret < 0) { 117 | fprintf(stderr, "Error muxing packet\n"); 118 | break; 119 | } 120 | av_free_packet(&pkt); 121 | } 122 | 123 | av_write_trailer(ofmt_ctx); 124 | 125 | end: 126 | avformat_close_input(&ifmt_ctx); 127 | 128 | /* close output */ 129 | if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) 130 | avio_closep(&ofmt_ctx->pb); 131 | 132 | avformat_free_context(ofmt_ctx); 133 | 134 | if (ret < 0 && ret != AVERROR_EOF) { 135 | fprintf(stderr, "Error failed to write packet to output file.\n"); 136 | return 1; 137 | } 138 | return 0; 139 | } -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoRemuxing/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_H_ 2 | #define _COMMON_H_ 3 | 4 | #include 5 | #include "VideoRemuxingHeader.h" 6 | #include "IOFiles.h" 7 | 8 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoScaling/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(.) 2 | add_executable(VideoScalingMain VideoScalingMain.cpp) 3 | target_link_libraries(VideoScalingMain ${LINK_LIBS} ${FFMPEG_LIBS}) -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoScaling/IOFiles.h: -------------------------------------------------------------------------------- 1 | #ifndef _IO_FILES_H_ 2 | #define _IO_FILES_H_ 3 | 4 | /************************************************* 5 | Struct: IOFiles 6 | Description: 接收命令行参数 7 | *************************************************/ 8 | typedef struct _IOFiles { 9 | char *inputName; //输入文件名 10 | char *outputName; //输出文件名 11 | char *inputFrameSize; //输入图像的尺寸 12 | char *outputFrameSize; //输出图像的尺寸 13 | 14 | FILE *iFile; //输入文件指针 15 | FILE *oFile; //输出文件指针 16 | 17 | } IOFiles; 18 | 19 | 20 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoScaling/VideoScalingHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef _VIDEO_SCALING_HEADER_H_ 2 | #define _VIDEO_SCALING_HEADER_H_ 3 | 4 | extern "C" 5 | { 6 | #include "libavutil/imgutils.h" 7 | #include "libavutil/parseutils.h" 8 | #include "libswscale/swscale.h" 9 | }; 10 | 11 | #endif -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoScaling/VideoScalingMain.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | 4 | /************************************************* 5 | Function: hello 6 | Description: 解析命令行传入的参数 7 | Calls: 无 8 | Called By: main 9 | Input: (in)argc : 默认命令行参数 10 | (in)argv : 默认命令行参数 11 | Output: (out)io_param : 解析命令行的结果 12 | Return: true : 命令行解析正确 13 | false : 命令行解析错误 14 | *************************************************/ 15 | static bool hello(int argc, char **argv, IOFiles &files) { 16 | printf("FFMpeg Scaling Demo.\nCommand format: %s input_file input_frame_size output_file output_frame_size\n", 17 | argv[0]); 18 | if (argc != 5) { 19 | printf("Error: command line error, please re-check.\n"); 20 | return false; 21 | } 22 | 23 | files.inputName = argv[1]; 24 | files.inputFrameSize = argv[2]; 25 | files.outputName = argv[3]; 26 | files.outputFrameSize = argv[4]; 27 | 28 | files.iFile = fopen(files.inputName, "rb+"); 29 | if (!files.iFile) { 30 | printf("Error: cannot open input file.\n"); 31 | return false; 32 | } 33 | 34 | files.oFile = fopen(files.outputName, "wb+"); 35 | if (!files.oFile) { 36 | printf("Error: cannot open output file.\n"); 37 | return false; 38 | } 39 | 40 | return true; 41 | } 42 | 43 | /************************************************* 44 | Function: read_yuv_from_ifile 45 | Description: 从输入文件中读取像素数据 46 | Calls: 无 47 | Called By: main 48 | Input: (in)srcWidth : 输入图像的宽度 49 | (in)srcHeight : 输入图像的的高度 50 | (in)color_plane :颜色分量:0——Y;1——U;2——V 51 | (in)files : 包含输入文件的结构 52 | Output: (out)src_data : 保存输入数据的缓存 53 | (out)src_linesize : 54 | Return: true : 命令行解析正确 55 | false : 命令行解析错误 56 | *************************************************/ 57 | static int read_yuv_from_ifile(uint8_t *src_data[4], int src_linesize[4], int srcWidth, int srcHeight, int color_plane, 58 | IOFiles &files) { 59 | int frame_height = color_plane == 0 ? srcHeight : srcHeight / 2; 60 | int frame_width = color_plane == 0 ? srcWidth : srcWidth / 2; 61 | int frame_size = frame_width * frame_height; 62 | int frame_stride = src_linesize[color_plane]; 63 | 64 | if (frame_width == frame_stride) { 65 | //宽度和跨度相等,像素信息连续存放 66 | fread(src_data[color_plane], 1, frame_size, files.iFile); 67 | } else { 68 | //宽度小于跨度,像素信息保存空间之间存在间隔 69 | for (int row_idx = 0; row_idx < frame_height; row_idx++) { 70 | fread(src_data[color_plane] + row_idx * frame_stride, 1, frame_width, files.iFile); 71 | } 72 | } 73 | 74 | return frame_size; 75 | } 76 | 77 | /************************************************* 78 | Function: main 79 | Description: 入口点函数 80 | *************************************************/ 81 | #define MAX_FRAME_NUM 100 82 | 83 | int main(int argc, char **argv) { 84 | int ret = 0; 85 | 86 | //解析命令行输入参数 87 | IOFiles files = {NULL}; 88 | if (!hello(argc, argv, files)) { 89 | fclose(files.iFile); 90 | fclose(files.oFile); 91 | return 0; 92 | } 93 | int srcWidth, srcHeight, dstWidth, dstHeight; 94 | if (av_parse_video_size(&srcWidth, &srcHeight, files.inputFrameSize)) { 95 | printf("Error: parsing input size failed.\n"); 96 | fclose(files.iFile); 97 | fclose(files.oFile); 98 | return 0; 99 | } 100 | if (av_parse_video_size(&dstWidth, &dstHeight, files.outputFrameSize)) { 101 | printf("Error: parsing output size failed.\n"); 102 | fclose(files.iFile); 103 | fclose(files.oFile); 104 | return 0; 105 | } 106 | 107 | //创建SwsContext结构 108 | enum AVPixelFormat src_pix_fmt = AV_PIX_FMT_YUV420P; 109 | enum AVPixelFormat dst_pix_fmt = AV_PIX_FMT_YUV420P; 110 | struct SwsContext *sws_ctx = sws_getContext(srcWidth, srcHeight, src_pix_fmt, dstWidth, dstHeight, dst_pix_fmt, 111 | SWS_BILINEAR, NULL, NULL, NULL); 112 | if (!sws_ctx) { 113 | printf("Error: allocating SwsContext struct failed.\n"); 114 | fclose(files.iFile); 115 | fclose(files.oFile); 116 | sws_freeContext(sws_ctx); 117 | 118 | return 0; 119 | } 120 | 121 | //分配input和output 122 | uint8_t *src_data[4], *dst_data[4]; 123 | int src_linesize[4], dst_linesize[4]; 124 | if ((ret = av_image_alloc(src_data, src_linesize, srcWidth, srcHeight, src_pix_fmt, 32)) < 0) { 125 | printf("Error: allocating src image failed.\n"); 126 | fclose(files.iFile); 127 | fclose(files.oFile); 128 | av_freep(&src_data[0]); 129 | av_freep(&dst_data[0]); 130 | sws_freeContext(sws_ctx); 131 | 132 | return 0; 133 | } 134 | if ((ret = av_image_alloc(dst_data, dst_linesize, dstWidth, dstHeight, dst_pix_fmt, 1)) < 0) { 135 | printf("Error: allocating dst image failed.\n"); 136 | fclose(files.iFile); 137 | fclose(files.oFile); 138 | av_freep(&src_data[0]); 139 | av_freep(&dst_data[0]); 140 | sws_freeContext(sws_ctx); 141 | 142 | return 0; 143 | } 144 | 145 | //从输出frame中写出到输出文件 146 | int dst_bufsize = ret; 147 | for (int idx = 0; idx < MAX_FRAME_NUM; idx++) { 148 | read_yuv_from_ifile(src_data, src_linesize, srcWidth, srcHeight, 0, files); 149 | read_yuv_from_ifile(src_data, src_linesize, srcWidth, srcHeight, 1, files); 150 | read_yuv_from_ifile(src_data, src_linesize, srcWidth, srcHeight, 2, files); 151 | 152 | sws_scale(sws_ctx, (const uint8_t *const *) src_data, src_linesize, 0, srcHeight, dst_data, dst_linesize); 153 | 154 | fwrite(dst_data[0], 1, dst_bufsize, files.oFile); 155 | } 156 | 157 | printf("Video scaling succeeded.\n"); 158 | 159 | fclose(files.iFile); 160 | fclose(files.oFile); 161 | av_freep(&src_data[0]); 162 | av_freep(&dst_data[0]); 163 | sws_freeContext(sws_ctx); 164 | 165 | return 0; 166 | } -------------------------------------------------------------------------------- /Tutorial/SDK_Demo/VideoScaling/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_H_ 2 | #define _COMMON_H_ 3 | 4 | #include 5 | #include "VideoScalingHeader.h" 6 | #include "IOFiles.h" 7 | 8 | #endif -------------------------------------------------------------------------------- /chapter01/01-音视频技术概述.md: -------------------------------------------------------------------------------- 1 | ## 01-音视频技术概述 2 | 3 | ### 1.1 音视频信息与多媒体系统 4 | 5 | 常用的开源工程有FFmpeg、GStreamer、WebRTC、LAV Filters 6 | 技术瓶颈主要有以下几点: 7 | 1.设备运算能力限制 8 | 运算能力不足会出现延迟 9 | 2.数据传输带宽限制 10 | 带宽不足影响显示质量和响应速度 11 | 3.显示设备设计限制 12 | 普通小屏显示,对视觉的生理成像机制适应性不足 13 | 14 | ### 1.2 典型的音视频与多媒体系统结构 15 | 16 | #### 1.2.1视频点播 17 | 18 | VOD(Video On Demand)视频点播 19 | PGC 专业内容生产 20 | UGC 用户生成内容 21 | CDN 内容分发网络 22 | 23 | pic1.jpg 24 | 25 | #### 1.2.2视频直播 26 | 27 | pic2.jpg 28 | 29 | #### 1.2.3安防监控 30 | 31 | pic3.jpg 32 | 33 | #### 1.2.4 34 | 35 | 许多基于公网的视频会议系统都以WebRTC为基础,以尽可能低的延迟提供高质量的音频和视频实时通信服务 36 | 37 | pic4.jpg 38 | -------------------------------------------------------------------------------- /chapter01/pic1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/chapter01/pic1.jpg -------------------------------------------------------------------------------- /chapter01/pic2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/chapter01/pic2.jpg -------------------------------------------------------------------------------- /chapter01/pic3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/chapter01/pic3.jpg -------------------------------------------------------------------------------- /chapter01/pic4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/chapter01/pic4.jpg -------------------------------------------------------------------------------- /chapter02/02-图像、像素与颜色空间.md: -------------------------------------------------------------------------------- 1 | ## 02-图像、像素与颜色空间 2 | 3 | 一个连续播放的视频文件是由一串连续的、前后存在相关关系的图像构成的, 4 | 并通过连续的图像中的内容以及图像间的相互关系表达整个视频文件所包含的信息。 5 | 这些组成视频基本单元的图像被称为帧,其在本质上与普通的静态图像没有任何区别, 6 | 只是在进行压缩编码的过程中使用了不同的技术,以达到更高的效率 7 | 8 | ### 2.1 图像与像素 9 | 10 | 图像一般值静态图像,图像是一种在二维平面上通过排列像素(Pixel)来表达信息的数据组织形式 11 | 像素是构成图像的基本单元,每个像素都表示图像中一个坐标位置上的亮度或色彩等信息 12 | 13 | 实际场景中,图像通常分为彩色图像(Color)和灰度图像(Gray)两种。 14 | 彩色图像中,每个像素都由多个颜色分量组成 15 | 灰度图像中,每个像素都只有一个分量用来表示该像素的灰度值 16 | 17 | 彩色图像转灰度图像: 18 | matlab 19 | Gray=0.2989*R+0.5870*G+0.1140*B 20 | opencv 21 | cvtColor(imgRGB,imgGray,COLOR_RGB2GRAY) 22 | 23 | ### 2.2 图像的位深与颜色空间 24 | 25 | 对于灰度图像,在每个像素点上只有一个分量,即该点的亮度值。 26 | 通常数据长度有8bit或者10bit两种。8bit[0,255] 10bit[0,1024] 27 | 28 | 对于彩色图像,其每个像素点都包含多个颜色分量,每个颜色分量被称为一个通道(Channel) 29 | 以RGB图像为例,如果每个通道的位深都是8bit,则每个像素需要24bit(8bit*3), 30 | 如果还包含图像透明度Alpha通道,则图像为ARGB,每个像素需要32bit(8bit*4) 31 | 32 | 确定图像的位深后,根据图像的高、宽尺寸可以确定图像的数据体积。 33 | 如:RGB图像1920*1080 则体积为 34 | 35 | $$ 36 | (8bit*3)*(1920*1080)=6220800B \approx 6.22MB $$ 37 | 38 | RGB格式的图像更适合用来显示,而不适合在图像处理系统中。 39 | 因此针对不同的场景有不同的彩色数据表达方式,即颜色空间 40 | 颜色空间是一种利用整数区间来表示不同颜色的模型,其维度可以分为一维、二维、三维甚至更高维 41 | 常用的三维空间有:RGB、CIEXYZ、YUV、HSV 42 | 43 | RGB (0,0,0)表示纯黑 (MAX,MAX,MAX)表示纯白。由于每个颜色都与三个分量相关, 44 | 并且各个分量之间不存在主次关系,所以无法针对次要信息进行特定的亚采样。因此不适用于视频信号压缩编码 45 | YUV Y亮度分量 UV色度分量。色度分量的采样率可以等于或低于亮度分量 46 | 在讨论图像与视频压缩场景下,通常默认YUV格式可等价于YCrCb格式。 47 | 48 | RGB与YUV互转 R,G,B[0,255] U,V[-128,128] 49 | RGB--->YUV 50 | matlab 51 | Y=(77*R+150*G+29*B)>>8 52 | U=((-44*R-87*G+131*B)>>8)+128 53 | V=((131*R-110*G-21*B)>>8)+128 54 | opencv 55 | cvtColor(imgRGB,imgYUV,COLOR_RGB2YUV_I420) 56 | YUV--->RGB 57 | matlab 58 | R=Y+((360 * (V-128))>>8) 59 | G=Y-(((88*(U-128)+184*(V-128)))>>8) 60 | B=Y+((455*(U-128))>>8) 61 | opencv 62 | cvtColor(imgYUV,imgRGB,COLOR_YUV2RGB_I420) 63 | 64 | ### 2.3 图像压缩编码 65 | 66 | 多种图像压缩算法,根据压缩后是否存在信息损失分为无损压缩和有损压缩两大类 67 | 无损压缩:TIFF、BMP、GIF、PNG 68 | 有损压缩:JPEG 69 | 70 | 各种图像压缩算法均非单一的算法,而是若干不同算法的组合,以此尽可能的提升数据的压缩效率 71 | 无损编码中常用的: 72 | 游程编码 73 | 特别适合处理信息元素集合较小(如二值化的图像,只包含0和1两个信息元)的信息 74 | 游程编码压缩数据量的主要思路是将一串连续的、重复的字符使用“数目”+“字符”的形式表示 75 | 如: 76 | AAAAABBCCCCCCDDAEE --->5A2B6C2D1A2E 77 | 在图像与语音信号中,出现连续字符的情况较为常见(如语音中用连续的0表示静默音频、图像中的单色或者相近色的背景等) 78 | BMP图像中可选游程编码对像素数据进行压缩 79 | 哈夫曼编码 80 | 哈夫曼编码是可变长编码方式的一种,该方法完全依赖于码字出现的概率, 81 | 是一种构造整体平均长度最短的编码方式,关键步骤就是建立符合哈夫曼编码规则的二叉树(哈夫曼树) 82 | 哈夫曼树是一种特殊的二叉树,其终端节点的个数与待编码的码元个数相同,而且在每个终端节点上都有各自的权值 83 | 每个终端节点的路径长度乘以该节点的权值的总和为整个二叉树的加权路径长度。 84 | 在满足条件的各种二叉树中,路径长度最短的二叉树即为哈夫曼树 85 | 实际编码中,码元的权值可以设置为其概率值 86 | 87 | pic1.jpg 88 | pic2.jpg 89 | pic3.jpg 90 | 91 | 得到哈夫曼树后,便可以得到每个码元的哈夫曼编码的码字,具体方法是:从哈夫曼 92 | 树的根节点开始遍历,直至每个终点节点,当访问某节点的左子树时赋予码字0, 93 | 当访问其右子树的时赋予码字1。直到遍历到终端节点,这一路径所代表的0和1的串 94 | 便是该码元的哈夫曼编码码字 95 | 上图的哈夫曼树,得到的编码码表: 96 | A:0000 97 | B:0001 98 | C:001 99 | D:10 100 | E:11 101 | F:01 102 | 从码表可以看出另外的一个规律,就是任意一个码字都不可能是其他码字的前缀。 103 | 因此哈夫曼编码的信息可以紧密排列且连续传输,而不用担心解码时出现歧义。 104 | 105 | 常见的图像压缩编码格式 106 | BMP Bitmap 可以保存位深为1bit、4bit、8bit、24bit或者32bit的图像,图像数据可以使用未压缩的RGB格式 107 | JPEG 基于离散余弦变换(Discrete Cosine Transform DCT)的有损压缩编码格式 108 | 可以通过较小的数据损失获得较小的数据体积。离散余弦变换具有以下特点: 109 | 1 变换后的频域能量分布与实际的图像信号更加吻合 110 | 2 更容易兼容硬件计算,运算更高效 111 | 把一副未压缩的图像压缩成JPEG格式的主要流程: 112 | 1.把像素格式的图像数据转换为YUV格式,并对两个亮度分量进行亚像素采样,最终生成4:2:0格式的图像数据 113 | 2.针对每个YUV格式的图像数据分别进行处理,将每个分量的图像等分为8像素*8像素的像素块 114 | 3.对于每个8*8的像素块,都使用离散余弦变换将像素数据变换为值频域,并根据给定的量化表将变换系数量化为特定值 115 | 4.对于某个分量图像的所有像素块,对其变换量化后的直流分量系数通过DPCM编码, 116 | 在交流分量系数通过“之”字型扫描转换为一维数据后,再通过游程编码进行处理。 117 | 5.对于处理后的变换系数,使用熵编码(哈夫曼编码等)生成压缩码流输出 118 | 119 | pic4.jpg -------------------------------------------------------------------------------- /chapter02/pic1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/chapter02/pic1.jpg -------------------------------------------------------------------------------- /chapter02/pic2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/chapter02/pic2.jpg -------------------------------------------------------------------------------- /chapter02/pic3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/chapter02/pic3.jpg -------------------------------------------------------------------------------- /chapter02/pic4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/chapter02/pic4.jpg -------------------------------------------------------------------------------- /chapter03/03-视频压缩编码.md: -------------------------------------------------------------------------------- 1 | ## 03-视频压缩编码 2 | 3 | ### 3.1 视频压缩编码的基础知识 4 | 5 | 与静态图像类似,数字视频中每幅图像都由呈平面紧密排列的像素矩阵组成,被称为视频帧, 6 | 视频中每秒内容u所包含的视频帧的数量称为帧率,单位fps(frame per second) 7 | 在视频压缩编码中,图像的颜色空间通常使用YCrCb颜色空间,在工程上常用YUV颜色空间指代 8 | 在视频帧中,每个像素所占的字节数由其采样方式和位深决定 9 | 10 | YUV像素格式 11 | 4:4:4 每1个Y均对应1个U和1个V 12 | 4:2:2 每2个Y对应1个U和1个V 13 | 4:2:0(又称4:1:1) 每4个Y对应1个U和1一V 14 | 使用这种方式的主要原因是人的感官对于亮度信息的敏感度远高于对色度信息的敏感度, 15 | 因此相对其他像素格式,YUV像素格式的最大优势就是可以适当地降低色度分量的采样率,并保证不对图像造成太大影响 16 | 17 | 从数字视频采集设备中获取的原始图像信号需要转换为某中间格式后,可能进行编码和传输等后续操作 18 | 通用中间格式(Common Intermediate Format CIF)为其他格式的基准, 19 | 其他常用格式: 20 | QCIF 176*144 移动多媒体 21 | CIF 352*288 视频会议与可视电话 22 | 4CIF/SD 720*576 标准清晰度 标清数字电视广播和DVD 23 | HD/720P 1280*720 高清晰度 高清数字电视广播和蓝光DVD 24 | FHD/1080P 1920*1080 全高清晰度 高清数字电视广播和蓝光DVD 25 | UHD 3840*2160(4K) 7680*4320(8K) 超高清晰度 超高清数字电视和高端数字娱乐系统 26 | 27 | ### 3.2 视频压缩编码标准的发展历程 28 | 29 | 从事视频编码算法的标准化组织 30 | ITU-T Internatial Telecommunication Union - Telecommunication standardization sector 31 | ISO International Standardization Organization 32 | 较强影响力的标准 MPEG-2 H.264 H.265等 33 | 比较有影响力的标准 VP8/VP9 VC-1 AVS AVS+ AVS2 34 | 35 | ### 3.3 视频压缩编码的基本原理 36 | 37 | 像素格式的视频数据之所以能被压缩,其根本原因在于视频中存在冗余信息 38 | 视频数据中的冗余信息主要有: 39 | 1.时间冗余 视频中相邻两帧之间的内容相似,存在运动关系 40 | 2.空间冗余 视频中某一帧内部的相邻像素存在相似性 41 | 3.编码冗余 视频中不同数据出现的概率不同 42 | 4.视觉冗余 人的视觉系统对视频中的不同部分敏感度不同 43 | 44 | 在视频编码中 45 | 预测编码: 46 | 帧内预测:根据当前帧已编码的数据进行预测,利用图像内相邻像素之间的相关性去除视频中的空间冗余 47 | 帧间预测:将部分已编码的图像作为参考帧,利用前后帧之间的时间相关性去除视频中的时间冗余 48 | 变换编码: 49 | 主要方法是离散余弦变换及其优化算法 50 | 熵编码: 51 | 在信息学领域,熵用来表示信息的混乱程序或相关性,熵越高,信息越无序、越不可预测。 52 | 常用熵编码算法: 53 | UVLC 指数哥伦布编码 常用于帧与Slice头信息的解析过程 54 | CAVLC 上下文自适应的变长编码 主要用于H.264的Baseline Profile等格式的宏块类型、变换系数等信息 55 | CABAC 上下文自适应的二进制算术编码 主要用于H.264的Main/High Profile和H.265等格式的宏块类型、变换系统等信息 56 | 57 | ### 3.4 视频编码标准H.264 58 | 59 | 严格来说,H.264是MPEG-4家族的一部分,即MPEG-4系列文档ISO-14496的第10部分。因此又被称为MPEG-4 AVC 60 | 61 | pic1.jpg 62 | 63 | ### 3.5 高效视频编码标准H.265 64 | 65 | pic2.jpg -------------------------------------------------------------------------------- /chapter03/pic1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/chapter03/pic1.jpg -------------------------------------------------------------------------------- /chapter03/pic2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/chapter03/pic2.jpg -------------------------------------------------------------------------------- /chapter04/04-音频压缩编码.md: -------------------------------------------------------------------------------- 1 | ## 04-音频压缩编码 2 | 3 | ### 4.1 音频压缩编码的基础知识 4 | 5 | 声音信息的三大要素: 6 | 1振幅 振幅越大,音量越高;振幅越小,音量越小 7 | 2频率 频率越高,音调越高,声音越尖锐;频率越低,音调越低,声音越低沉。可听音频率范围20Hz-20kHz 8 | 低于20hz称次声波,高于20kHz称超声波 9 | 3音色 组成声音信号的各个频率的声音分量的强度不尽相同,其中强度最大的频率分量由声源主体振动产生, 10 | 称为“基因”,声源其他部分的振动同样产生频率不同、强度稍低的声音分量,称为“泛音”。音色主要由 11 | 泛音的特性决定,不同的泛音使我们可以根据感知对声音进行划分:男声、女声、童声、钢琴音等 12 | 13 | ### 4.2 音频信息采样与数字化 14 | 15 | 数字音频常用的媒介 CD compat disk 16 | 音频采样的常用频率是44.1kHz 17 | 音频的量化,又称为位深或者位宽,表示以多大的数据量来表示一个量化后的数据常用的 4bit 8bit 16bit 32bit 18 | 19 | ### 4.3 脉冲编码调制 20 | 21 | PCM 脉冲编码调制 22 | A律13折线 23 | u律15折线 24 | 25 | ### 4.4 MP3格式与MP3编码标准 26 | 27 | MP3定义于MPEG-1标准集合的第三部分,即MPEG-1 Audio Layer 3 28 | 一个MP3文件以帧(Frame)为单位保存音频的码流数据,每帧数据都由帧头和载荷数据构成。 29 | 除保存在帧中的音频码流数据外,MP3文件还定义了两个标签结构, 30 | 用来保存歌曲名称、作者、专辑和年份等音频文件的属性信息,并形成ID3标签 31 | 常用的ID3标签 32 | ID3V1 Tag 文件尾 33 | ID3V2 Tag 文件头 34 | ----TagV1 35 | 固定长度128B,其结构如下 36 | typedef struct{ 37 | char id[3];//"TAG" 38 | char titile[30];//音频媒体的标题 39 | char author[30];//作者 40 | char album[30];//专辑 41 | char year[4];//年份 42 | char note[30];//注释 43 | char style;//节目流派 44 | }ID3V1 45 | 46 | ----TagV2 47 | typedef struct { 48 | char file_id[3];//ID3 49 | char version;//主版本号 50 | char reversion;//次版本号 51 | char flags;//Tag的标识位,仅最高3bit有效 使用非同步编码-包含扩展标签头信息-该Tag为实验性标准,未正式发布 52 | char size[4];//Tag中有效数据的大小(不包括Tag标签头),需要注意的是,计算实际长度的时候,每字节抛弃最高位,由剩余的7位按大端字节组 53 | }TagHeader//标签头 54 | 在标签头后,是若干的标签帧,每个标签帧又是由标签帧头和标签帧数据组成 55 | typedef struct{ 56 | char frame_id[4];//常见id TIT2:标题 TPE1:作者 TABL:专辑 TYER:年份 TRAK:音轨、集合中的位置 57 | char size[4];// 58 | char flags[2]; 59 | }TagFrameHeader//标签帧头 60 | 61 | 帧内载荷数据:针对MPEG-1 Audio标准 Layer1规定每帧保存384个采样值, 62 | layer2和layer3规定每帧保存1152个采样值。 63 | 如果确定了音频的采样率,就可以计算每帧的持续时间:采样率44.1kHz的每帧持续时间 64 | 1152/44100*1000= 26ms 65 | MP3帧由帧头(Header)和帧数据(Side Data、Main Data、Ancillary Data)组成 66 | typedef struct{ 67 | unsigned int sync:11;//同步标识,每个bit都是1 68 | unsigned int version:2;//版本信息,00:MPEG-2.5 01:未定义 10:MPEG-2 11:MPEG-1 69 | unsigned int layer:2;//层信息, 00:未定义 01:layer3 10:layer2 11:layer1 70 | unsigned int errorprotection:1;//CRC校验,0:开启校验 1:禁用校验 71 | unsigned int bitrate_index:4;//码率, 72 | unsigned int sampling_frequency:2;//采样率 73 | unsigned int padding:1;//填充标识, 0:未使用填充位 1:使用填充位 74 | unsigned int private:1;//保留位 75 | unsigned int mode:2;//声道模式,00:立体音 01:联合立体音 10:双声道 11:单声道 76 | unsigned int modeextension:2;//扩展声道模式 77 | unsigned int copyright:1;//版权标识 78 | unsigned int original:1;//原始媒体标识 79 | unsigned int emphasis:2;//强调方式标识,00:none 01:50/15ms 10:reserved 11:CCITJ.17 80 | }FrameHeader 81 | 82 | 帧数据(Side Data、Main Data、Ancillary Data) 83 | 1.Side Data 保存部分机会解码Main Data所需信息,字节数:单声道17B 多声道和立体音32B 84 | 2.Main Data 保存实际编码后的音频采样值 85 | 3.Ancillary Data 可选结构,不显示地指定长度 86 | 87 | MP3用到的主要模块 88 | 1,多项子带滤波器组 89 | 对一帧内的1152个PCM音频采样数据进行编码时,通过一个多项子带滤波器组,将0-22.05kHz的频率范围 90 | 分割为32个频率子带,每个子带大概689Hz,每个子带仅保留1/32个采样点数,即36个 91 | 2,信号加窗与MDCT 92 | 对多项子带滤波器组的输出,先加窗处理,4种窗类型:长窗、开始窗、短窗、结束窗。 93 | 再使用改进离散余弦变换(MDCT)进行变换,这样每个频率子带又进一步被分割成 94 | 18个频率线,总共576个频率线,对应一帧中的一个编码颗粒。 95 | 96 | 3,心理声学模型 97 | 人对声音的感知并非完全取决于声音的频率和强度等信息,而是根据声源和环境动态适应,此现象即人的听觉掩蔽效应。 98 | MPEG-1 Audio提供了两种心理声学模型 99 | 模型1 运算简单,适用于高码率场景 100 | 模型2 运算复杂,适用于低码率场景,是MP3编码的推荐选项 101 | 音频采样值通过多项子带滤波器输出后,通过1024点和256点FFT后,转换为频域,通过分析频域信号,心理声学模型 102 | 产生以下信息供后续编码使用: 103 | 感知熵 PerceptualEntropy PE 用于判定信号窗的切换 104 | 信掩比 Signal to MaskRatio SMR 决定量化编码时的比特数分配 105 | 4,非均匀量化 106 | 音频信号采样在经过加窗和MDCT后,需要在编码前对获取的频率线信息进行量化处理。 107 | MP3使用的是非均匀量化,非均匀量化需要两类输入信息:MDCT输出的频率线信息、心理声学模型输出的掩蔽信息 108 | 在编码时,量化与编码一次处理一个编码颗粒中的576个采样的频域采样值,处理流程分两层嵌套的循环处理 109 | 码率控制循环,即内层循环 110 | 混叠控制循环,即外层循环 111 | 内层循环执行频域数据的实际量化过程,量化值的取值范围和量化步长呈负相关,即相同的频域数据,量化步长越大, 112 | 输出的量化数据越小,反之,步长越小,输出的量化值越大。(主要是哈夫曼编码的取值范围有限制,适应它) 113 | 外层循环控制内层循环在量化过程中产生的量化失真。 114 | 5,熵编码 115 | MP3编码器对量化的输出数据使用哈夫曼编码进行熵编码。 116 | 117 | ### 4.5 AAC格式与AAC编码标准 118 | 119 | AAC标准协议中,定义了两种AAC格式 120 | 1.ADIF格式 Audio Data Interchange Format 音频数据交换格式 121 | 2.ADTS格式 Audio Data Transport Stream 音频数据传输流 122 | 123 | ADIF格式 124 | 一个ADIF格式的音频文件中通常包含一个单独的ADIF Header(文件头)和一个完整的Raw Data Stream(音频流数据) 125 | ADIF Header 126 | 字段 bits 含义 127 | adif_id 32 ADIF格式的标识字段,固定值为0x41444946 128 | copyright_id_present 1 版权标识设置 129 | copyright_id 72 版权标识 130 | original_copy 1 原版或复制版标识 131 | home 1 内容原创标识 132 | bitstream_type 1 媒体流类型:0 CBR 1 VBR 133 | bitrate 23 CBR模式表示指定码率,VBR模式表示最高码率 134 | num_program_config_elements 4 program_config_elements结构的数量 135 | adif_buffer_fulness 20 program_config_elements前的码流填充位 136 | program_config_elements() - program_config_elements结构 137 | 138 | program_config_elements 139 | 字段 bits 含义 140 | element_instance_tag 4 141 | object_type 2 142 | sampling_frequency_index 4 143 | num_front_channel_elements 4 144 | num_side_channel_elements 4 145 | num_back_channel_elements 4 146 | num_lfe_channel_elements 2 147 | num_assoc_data_elements 3 148 | num_valid_cc_elements 4 149 | mono_mixdown_present 1 150 | if (mono_mixdown_present == 1) 151 | mono_mixdown_element_number 4 152 | stereo_mixdown_present 1 153 | if (stereo_mixdown_present == 1) 154 | stereo_mixdown_element_number 4 155 | matrix_mixdown_idx_present 1 156 | if (matrix_mixdown_idx_present == 1){ 157 | matrix_mixdown_idx 2 158 | pseudo_surround_enable 1 159 | } 160 | for (i=0;i> test_mp4.csv 40 | ffprobe -show_frames -of flat=sep_char='-' -i test.mp4 41 | ffprobe -show_frames -of ini -i test.mp4 42 | ffprobe -show_frames -of json=c=1 -i test.mp4 43 | 44 | ### 7.4 ffmpeg的基本使用方法 45 | 46 | 显示版本 ffmpeg -version 47 | 查看编译配置参数 ffmpeg -buildconf 48 | 显示支持的解复用器格式 ffmpeg -demuxers 49 | 显示支持的复用器格式 ffmpeg -nuxers 50 | 显示支持的所有输入格式和输出格式 ffmpeg -formats 51 | 显示支持的解码器 ffmpeg -decoders 52 | 显示支持的编码器 ffmpeg -encoders 53 | 显示支持的媒体协议 ffmpeg -protocols 54 | 显示支持的硬件加速框架 ffmpeg -hwaccels 55 | 具体的命令较为复杂,详看 http://ffmpeg.org/ffmpeg.html 56 | -------------------------------------------------------------------------------- /chapter08/08-滤镜图.md: -------------------------------------------------------------------------------- 1 | ## 08-滤镜图 2 | 3 | libavfilter 提供了不同的滤镜来对视频信息和音频信息进行编辑。滤镜处理是在转码过程中由解码器输出的,尚未进行编码 4 | 的未压缩图像和音频采样数据。在编辑过程中,通常是将多个滤镜组成一个滤镜图来实现更强大的功能。 5 | 6 | ### 8.1 ffmpeg音视频滤镜 7 | 8 | 显示当前支持的所有滤镜 ffmpeg -filters 9 | -filter:v 等效于-vf 指定对输入文件中的视频流进行滤镜操作 10 | -filter:a 等效于-af 指定对输入文件中的音频流进行滤镜操作 11 | 12 | ### 8.2 简单滤镜图的应用 13 | 14 | 水平镜像翻转 ffmpeg -i input.mp4 -vf "hflip" output.mp4 15 | 垂直镜像翻转 ffmpeg -i input.mp4 -vf "vflip" output.mp4 16 | 视频缩放 ffmpeg -i input.mp4 -vf scale=640x480 -y output.mp4 17 | 视频画面旋转 把输入视频按顺时针方向旋转90度 ffmpeg -i input.mp4 -vf transpose=dir-clock -y output.mp4 18 | 视频图像滤波 19 | 视频图像锐化 20 | 视频画面裁剪 21 | 视频时间裁剪 22 | 为视频添加渐入渐出效果 23 | 设置视频帧率 ffmpeg -i input.mp4 -vf fps=fps=30 -y output.mp4 24 | 间隔抽取子视频帧 25 | 给视频添加水印 26 | 音频回声 27 | 音频淡入淡出效果 28 | 音频循环 29 | 音频剪裁 30 | 音频音量的检测与调节 31 | 32 | ### 8.3 复合滤镜图的应用 33 | 34 | 视频画面融合 35 | 视频图像叠加 36 | 视频拼接 37 | 音频混合 38 | 音频声道的混合运算和提取 -------------------------------------------------------------------------------- /chapter09/09-流媒体应用.md: -------------------------------------------------------------------------------- 1 | ## 09-流媒体应用 2 | 3 | 比较著名的有SRS live555 EasyDarwin Nginx+nginx-rtmp-module 4 | 5 | ### 9.1 构建SRS流媒体服务 6 | 7 | SRS 全称 Simple Realtime Streaming Server 8 | 9 | ### 9.2 构建NginxRTMP流媒体服务 10 | -------------------------------------------------------------------------------- /chapter10/10-FFmpeg SDK的使用.md: -------------------------------------------------------------------------------- 1 | ## 10-FFmpeg SDK的使用 2 | 3 | ### 10.1 使用CMake构建工程 4 | 5 | ### 10.2 FFmpeg SDK基本使用方法示例:获取目录下的文件信息 6 | 7 | 可以对照源码中doc/examples/avio_list_dir.c -------------------------------------------------------------------------------- /chapter10/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(cmd_dir cmd_dir.cpp ) 2 | target_link_libraries(cmd_dir ${FFMPEG_LIBS} ${LINK_LIBS}) -------------------------------------------------------------------------------- /chapter10/cmd_dir.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by lining on 3/1/22. 3 | // 4 | 5 | extern "C" { 6 | #include 7 | #include 8 | #include 9 | } 10 | 11 | #include 12 | 13 | using namespace std; 14 | 15 | static void usage(const char *program_name) { 16 | cout << "usage: " << string(program_name) << " input_dir" << endl; 17 | cout << "API example program to show how to list files in directory accessed through AVIOContext." << endl; 18 | } 19 | 20 | static string type_string(int type) { 21 | switch (type) { 22 | case AVIO_ENTRY_DIRECTORY: 23 | return ""; 24 | case AVIO_ENTRY_FILE: 25 | return ""; 26 | case AVIO_ENTRY_BLOCK_DEVICE: 27 | return ""; 28 | case AVIO_ENTRY_CHARACTER_DEVICE: 29 | return ""; 30 | case AVIO_ENTRY_NAMED_PIPE: 31 | return ""; 32 | case AVIO_ENTRY_SYMBOLIC_LINK: 33 | return ""; 34 | case AVIO_ENTRY_SOCKET: 35 | return ""; 36 | case AVIO_ENTRY_SERVER: 37 | return ""; 38 | case AVIO_ENTRY_SHARE: 39 | return ""; 40 | case AVIO_ENTRY_WORKGROUP: 41 | return ""; 42 | case AVIO_ENTRY_UNKNOWN: 43 | default: 44 | break; 45 | } 46 | return ""; 47 | } 48 | 49 | 50 | static int list_op(const char *input_dir) { 51 | AVIODirEntry *entry = nullptr; 52 | AVIODirContext *ctx = nullptr; 53 | int cnt, ret; 54 | char filemode[4], uid_and_gid[20]; 55 | 56 | ret = avio_open_dir(&ctx, input_dir, nullptr); 57 | if (ret < 0) { 58 | av_log(nullptr, AV_LOG_ERROR, "Cannot open directory.\n"); 59 | goto fail; 60 | } 61 | cnt = 0; 62 | 63 | for (;;) { 64 | ret = avio_read_dir(ctx, &entry); 65 | if (ret < 0) { 66 | av_log(nullptr, AV_LOG_ERROR, "Cannot list directory.\n"); 67 | goto fail; 68 | } 69 | if (!entry) { 70 | break; 71 | } 72 | if (entry->filemode == -1) { 73 | snprintf(filemode, 4, "???"); 74 | } else { 75 | snprintf(filemode, 4, "%3" PRIo64, entry->filemode); 76 | } 77 | 78 | snprintf(uid_and_gid, 20, "%" PRId64 "(%" PRId64 ")", entry->user_id, entry->group_id); 79 | 80 | if (cnt == 0) { 81 | av_log(nullptr, AV_LOG_INFO, "%-9s %12s %30s %10s %s %16s %16s %16s\n", "TYPE", "SIZE", "NAME", "UID(GID)", 82 | "UGO", "MODIFIED", "ACCESSED", "STATUS_CHANGED"); 83 | } 84 | av_log(nullptr, AV_LOG_INFO, "%-9s %12" PRId64 " %30s %10s %s %16" PRId64 " %16" PRId64 " %16" PRId64 "\n", 85 | type_string(entry->type).c_str(), 86 | entry->size, entry->name, uid_and_gid, filemode, entry->access_timestamp, 87 | entry->status_change_timestamp); 88 | avio_free_directory_entry(&entry); 89 | cnt++; 90 | } 91 | 92 | fail: 93 | avio_close_dir(&ctx); 94 | return ret; 95 | } 96 | 97 | int main(int argc, char **argv) { 98 | int ret; 99 | av_log_set_level(AV_LOG_DEBUG);//设置日志级别为debug 100 | if (argc > 2) { 101 | //输出帮助信息 102 | usage(argv[0]); 103 | return 1; 104 | } 105 | avformat_network_init(); 106 | ret = list_op(argv[1]); 107 | avformat_network_deinit(); 108 | return ret < 0 ? 1 : 0; 109 | } 110 | -------------------------------------------------------------------------------- /chapter11/11-使用FFmpeg SDK进行视频编解码.md: -------------------------------------------------------------------------------- 1 | ## 11-使用FFmpeg SDK进行视频编解码 2 | 3 | 重点介绍如何使用libavcodec将图像序列编码为H.264的视频码流,以及如何将视频码流解码为YUV格式图像。 4 | 并介绍libavcodec中常用的关键数据结构的定义和作用 5 | 6 | ### 11.1 libavcodec 视频编码 7 | 8 | 可以对照源码中doc/examples/encode_video.c 9 | 10 | avcodec_find_encoder_by_name 传入字符串类型的编码器名称即可查找对应的编码器实例, 11 | 具体的字符串内容,参考源码libavcodec/allcodecs.c中AVCodec类型的ff_xxxx_encoder 12 | 如:传入libx264 使用x264编码器编码 13 | 传入h264_nvenc 使用NVIDIA H.264编码器编码 14 | 类似的功能还有avcodec_find_encoder 传入AVCodecID 枚举类型 15 | 在FFmpeg中,没一个编码器对应一个上下文结构(AVCodecContext), 16 | 在编码开始前,通过avcodec_alloc_context3创建,再通过该上下文结构配置相应的编码参数 17 | AVCodecContext结构使用成员priv_data保存编解码器的配置信息,可以通过av_opt_set等方法进行设置 18 | avcodec_open2初始化编码器上下文,它会给AVCodecContext内部的数据成员分配内存空间, 19 | 以进行编码参数的校验,并调用编码器内部的init函数进行初始化操作 20 | AVFrame 未压缩的图像 21 | 针对视频编码器,其流程为从数据源获取图像格式的输入数据,保存为AVFrame对象,并传入编码器,从编码器中输出AVPacket结构 22 | AVFrame结构中data指针所保存的内存为待编码图像的像素数据,linesize获取data[]存储区的宽度 23 | AVPacket 压缩的视频码流 24 | 码流数据保存在AVPacket结构的data指针指向的内存区中,size为数据长度 25 | av_packet_alloc 创建AVPacket并初始化 26 | av_frame_alloc 创建AVFrame并初始化 av_frame_get_buffer 按照参数给AVFrame分配内存空间 27 | encoding 编码循环体,至少实现以下三个功能 28 | 1.从视频源中循环获取输入图像(如从输入文件中读取) 29 | 2.将当前帧传入编码器进行编码,获取输出的码流包 30 | 3.输出码流包中的压缩码流(如写出到输出文件) 31 | 编码1帧图像数据,需要两个关键的API 32 | avcodec_send_frame 将图像传入编码器 33 | avcodec_receive_packet 从编码器获取输出的码流 34 | 35 | ### 11.2 libavcodec 视频解码 36 | 37 | 可以对照源码中doc/examples/decode_video.c 38 | 39 | AVCodecParserContext 是码流解析器的句柄,其作用是从一串二进制数据流中解析出符合某种编码标准的码流包 40 | 使用av_parser_init可以根据指定的codec_id创建AVCodecParserContext 41 | decoding 解码循环体,至少实现以下三个功能 42 | 1.从输入源循环获取码流包(如从输入文件中读取码流包) 43 | 2.将当前帧传入解码器,获得输出的图像帧 44 | 3.输出解码获得的图像帧(如将图像帧写入输出文件) 45 | 想要从数据缓存区中解析出AVPacket结构,必须调用av_parser_parse2,当调用时,首先通过参数指定保存某一段码流数据 46 | 的缓存区及其长度,然后通过输出poutbuf指针或poutbuf_size的值来判断是否读取了一个完整的AVPacket结构 47 | 解码阶段,使用两个关键的API 48 | avcodec_send_packet 将封装的二进制码流传入解码器 49 | avcodec_receive_frame 从解码器中获取解码输出的图像帧结构 -------------------------------------------------------------------------------- /chapter11/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ##video_encoder 2 | add_executable(video_encoder video_encoder.cpp 3 | ../src/io_data.cpp 4 | ../src/video_encoder_core.cpp) 5 | target_link_libraries(video_encoder ${FFMPEG_LIBS} ${LINK_LIBS}) 6 | 7 | ##video_decoder 8 | add_executable(video_decoder video_decoder.cpp 9 | ../src/io_data.cpp 10 | ../src/video_decoder_core.cpp) 11 | target_link_libraries(video_decoder ${FFMPEG_LIBS} ${LINK_LIBS}) -------------------------------------------------------------------------------- /chapter11/video_decoder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "io_data.h" 6 | #include "video_decoder_core.h" 7 | 8 | static void usage(const char *program_name) { 9 | std::cout << "usage: " << std::string(program_name) 10 | << " input_file output_file" << std::endl; 11 | } 12 | 13 | int main(int argc, char **argv) { 14 | if (argc < 3) { 15 | usage(argv[0]); 16 | return 1; 17 | } 18 | 19 | char *input_file_name = argv[1]; 20 | char *output_file_name = argv[2]; 21 | 22 | std::cout << "Input file:" << std::string(input_file_name) << std::endl; 23 | std::cout << "output file:" << std::string(output_file_name) << std::endl; 24 | 25 | int32_t result = open_input_output_files(input_file_name, output_file_name); 26 | if (result < 0) { 27 | return result; 28 | } 29 | 30 | result = init_video_decoder(); 31 | if (result < 0) { 32 | return result; 33 | } 34 | 35 | result = decoding(); 36 | if (result < 0) { 37 | return result; 38 | } 39 | 40 | destroy_video_decoder(); 41 | close_input_output_files(); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /chapter11/video_encoder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by lining on 3/1/22. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "io_data.h" 10 | #include "video_encoder_core.h" 11 | 12 | static void usage(const char *program_name) { 13 | std::cout << "usage: " << std::string(program_name) 14 | << " input_yuv output_file codec_name" << std::endl; 15 | } 16 | 17 | int main(int argc, char **argv) { 18 | if (argc < 4) { 19 | usage(argv[0]); 20 | return 1; 21 | } 22 | 23 | char *input_file_name = argv[1]; 24 | char *output_file_name = argv[2]; 25 | char *codec_name = argv[3]; 26 | 27 | std::cout << "Input file:" << std::string(input_file_name) << std::endl; 28 | std::cout << "output file:" << std::string(output_file_name) << std::endl; 29 | std::cout << "codec name:" << std::string(codec_name) << std::endl; 30 | 31 | int32_t result = open_input_output_files(input_file_name, output_file_name); 32 | if (result < 0) { 33 | return result; 34 | } 35 | result = init_video_encoder(codec_name); 36 | if (result < 0) { 37 | goto failed; 38 | } 39 | result = encoding(50); 40 | if (result < 0) { 41 | goto failed; 42 | } 43 | 44 | failed: 45 | destroy_video_encoder(); 46 | close_input_output_files(); 47 | return 0; 48 | } -------------------------------------------------------------------------------- /chapter12/12-使用FFmpeg SDK进行音频编解码.md: -------------------------------------------------------------------------------- 1 | ## 12-使用FFmpeg SDK进行音频编解码 2 | 3 | 着重介绍如何调用libavcodec库中相关API,将PCM格式的原始音频采样数据编码为MP3格式或者AAC格式的音频文件,以及 4 | 将MP3格式或者AAC格式的音频文件解码为PCM格式的音频采样数据 5 | 6 | ### 12.1 libavcodec 音频编码 7 | 8 | 可以对照源码中doc/examples/encode_audio.c,但是有很大不同,跟本工程的video_encoder类似 9 | 10 | 初始化和视频编码器类似,但是传入的参数为音频采样格式、音频采样率、声道布局和声道数等信息 11 | 音频采样格式可分为packed和planar两大类,在每个大类中,根据保存采样点的数据类型又细分若干类型 12 | 单声道音频,packed和planar保存方式无实际区别 13 | 多声道、立体音频,packed格式保存的采样数据,各个声道之间交替存储。planar格式是不同声道连续存储 14 | 15 | ### 12.2 libavcodec 音频解码 16 | 17 | 可以对照源码中doc/examples/decode_audio.c,但是有很大不同,跟本工程的video_decoder类似 -------------------------------------------------------------------------------- /chapter12/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ##audio_encoder 2 | add_executable(audio_encoder audio_encoder.cpp 3 | ../src/io_data.cpp 4 | ../src/audio_encoder_core.cpp) 5 | target_link_libraries(audio_encoder ${FFMPEG_LIBS} ${LINK_LIBS}) 6 | 7 | ##audio_decoder 8 | add_executable(audio_decoder audio_decoder.cpp 9 | ../src/io_data.cpp 10 | ../src/audio_decoder_core.cpp) 11 | target_link_libraries(audio_decoder ${FFMPEG_LIBS} ${LINK_LIBS}) -------------------------------------------------------------------------------- /chapter12/audio_decoder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "audio_decoder_core.h" 6 | #include "io_data.h" 7 | 8 | static void usage(const char *program_name) { 9 | std::cout << "usage: " << std::string(program_name) 10 | << " input_file output_file audio_format(MP3/AAC)" << std::endl; 11 | } 12 | 13 | int main(int argc, char **argv) { 14 | if (argc < 3) { 15 | usage(argv[0]); 16 | return 1; 17 | } 18 | 19 | char *input_file_name = argv[1]; 20 | char *output_file_name = argv[2]; 21 | 22 | std::cout << "Input file:" << std::string(input_file_name) << std::endl; 23 | std::cout << "output file:" << std::string(output_file_name) << std::endl; 24 | 25 | int32_t result = open_input_output_files(input_file_name, output_file_name); 26 | if (result < 0) { 27 | return result; 28 | } 29 | 30 | result = init_audio_decoder(argv[3]); 31 | if (result < 0) { 32 | return result; 33 | } 34 | 35 | result = audio_decoding(); 36 | if (result < 0) { 37 | return result; 38 | } 39 | 40 | destroy_audio_decoder(); 41 | 42 | close_input_output_files(); 43 | return 0; 44 | } -------------------------------------------------------------------------------- /chapter12/audio_encoder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "audio_encoder_core.h" 6 | #include "io_data.h" 7 | 8 | static void usage(const char *program_name) { 9 | std::cout << "usage: " << std::string(program_name) 10 | << " input_yuv output_file codec_name" << std::endl; 11 | } 12 | 13 | int main(int argc, char **argv) { 14 | if (argc < 4) { 15 | usage(argv[0]); 16 | return 1; 17 | } 18 | 19 | char *input_file_name = argv[1]; 20 | char *output_file_name = argv[2]; 21 | char *codec_name = argv[3]; 22 | 23 | std::cout << "Input file:" << std::string(input_file_name) << std::endl; 24 | std::cout << "output file:" << std::string(output_file_name) << std::endl; 25 | std::cout << "codec name:" << std::string(codec_name) << std::endl; 26 | 27 | int32_t result = open_input_output_files(input_file_name, output_file_name); 28 | if (result < 0) { 29 | return result; 30 | } 31 | 32 | result = init_audio_encoder(argv[3]); 33 | if (result < 0) { 34 | return result; 35 | } 36 | result = audio_encoding(); 37 | if (result < 0) { 38 | goto failed; 39 | } 40 | 41 | failed: 42 | destroy_audio_encoder(); 43 | close_input_output_files(); 44 | return 0; 45 | } -------------------------------------------------------------------------------- /chapter13/13-使用FFmpeg SDK进行音视频文件的解封装与封装.md: -------------------------------------------------------------------------------- 1 | ## 13-使用FFmpeg SDK进行音视频文件的解封装与封装 2 | 3 | 调用libavformat库中相关的API,对音视频文件进行解封装和封装 4 | 5 | ### 13.1 音视频文件的解封装 6 | 7 | 可以对照源码中doc/examples/demuxing_decoding.c 8 | 9 | AVFormatContext 文件上下文结构 10 | avformat_open_input 打开输入的音视频文件或者网络媒体流,并初步探测输入数据的格式 11 | 解析与某个封装格式对应的头文件中的流信息 12 | avformat_find_stream_info 解析输入文件中的音视频流信息 13 | AVStream 用于表示音视频输入文件中所包含的一路音频流、视频流或字幕流 14 | av_find_best_stream 方便选择关注的流序号 15 | 16 | ### 13.2 音频流与视频流文件的封装 17 | 18 | 可以对照源码中doc/examples/muxing.c 但是这个是从图片读起,和音频混合。 19 | 也可以参照源码中doc/examples/remuxing.c 但是也不太相同 20 | 21 | 将音频流和视频流复用到输出文件需要三步 22 | 1.写入输出文件的头结构 23 | 2.循环写入音频包和视频包 24 | 将音频包和视频包循环写入输出文件分以下三步 25 | 1.从输入文件中读取音频包或者视频包 26 | 2.确定音频包和视频包的时间戳,判断写入顺序 27 | 3.将码流包写入输出文件 28 | 3.写入输出文件的尾结构 29 | -------------------------------------------------------------------------------- /chapter13/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ##demuxer 2 | add_executable(demuxer demuxer.cpp 3 | ../src/demuxer_core.cpp) 4 | target_link_libraries(demuxer ${FFMPEG_LIBS} ${LINK_LIBS}) 5 | 6 | ##muxer 7 | add_executable(muxer muxer.cpp 8 | ../src/muxer_core.cpp) 9 | target_link_libraries(muxer ${FFMPEG_LIBS} ${LINK_LIBS}) -------------------------------------------------------------------------------- /chapter13/demuxer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "demuxer_core.h" 6 | 7 | static void usage(const char *program_name) { 8 | std::cout << "usage: " << std::string(program_name) 9 | << " input_file output_video_file output_audio_file " << std::endl; 10 | } 11 | 12 | int main(int argc, char **argv) { 13 | if (argc < 4) { 14 | usage(argv[0]); 15 | return 1; 16 | } 17 | do { 18 | int32_t result = init_demuxer(argv[1], argv[2], argv[3]); 19 | if (result < 0) { 20 | break; 21 | } 22 | result = demuxing(argv[2], argv[3]); 23 | } while (0); 24 | 25 | end: 26 | destroy_demuxer(); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /chapter13/muxer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "muxer_core.h" 6 | 7 | static void usage(const char *program_name) { 8 | std::cout << "usage: " << std::string(program_name) 9 | << " video_file audio_file output_file " << std::endl; 10 | } 11 | 12 | int main(int argc, char **argv) { 13 | if (argc < 4) { 14 | usage(argv[0]); 15 | return 1; 16 | } 17 | int32_t result = 0; 18 | do { 19 | result = init_muxer(argv[1], argv[2], argv[3]); 20 | if (result < 0) { 21 | break; 22 | } 23 | result = muxing(); 24 | if (result < 0) { 25 | break; 26 | } 27 | 28 | } while (0); 29 | destroy_muxer(); 30 | 31 | return result; 32 | } 33 | -------------------------------------------------------------------------------- /chapter14/14-使用FFmpeg SDK添加视频滤镜和音频滤镜.md: -------------------------------------------------------------------------------- 1 | ## 14-使用FFmpeg SDK添加视频滤镜和音频滤镜 2 | 3 | 音视频滤镜的应用场景:美颜、变声 4 | 5 | ### 14.1 视频滤镜 6 | 7 | 可以对照源码中doc/examples/filtering_video.c 8 | 9 | AVFilterGraph 滤镜图结构,视频滤镜最核心的功能 10 | avfilter_graph_alloc 创建并初始化一个滤镜图结构 11 | 12 | ### 14.2 音频滤镜 13 | 14 | 可以对照源码中doc/examples/filter_audio.c 15 | -------------------------------------------------------------------------------- /chapter14/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ##video_filter 2 | add_executable(video_filter video_filter.cpp 3 | ../src/io_data.cpp 4 | ../src/video_filter_core.cpp) 5 | target_link_libraries(video_filter ${FFMPEG_LIBS} ${LINK_LIBS}) 6 | 7 | ##audio_filter 8 | add_executable(audio_filter audio_filter.cpp 9 | ../src/io_data.cpp 10 | ../src/audio_filter_core.cpp) 11 | target_link_libraries(audio_filter ${FFMPEG_LIBS} ${LINK_LIBS}) -------------------------------------------------------------------------------- /chapter14/audio_filter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "audio_filter_core.h" 6 | #include "io_data.h" 7 | 8 | static void usage(const char *program_name) { 9 | std::cout << "usage: " << std::string(program_name) 10 | << " input_file output_file " << std::endl; 11 | } 12 | 13 | int main(int argc, char **argv) { 14 | if (argc < 4) { 15 | usage(argv[0]); 16 | return -1; 17 | } 18 | 19 | char *input_file_name = argv[1]; 20 | char *output_file_name = argv[2]; 21 | char *volume_factor = argv[3]; 22 | 23 | int32_t result = 0; 24 | do { 25 | result = open_input_output_files(input_file_name, output_file_name); 26 | if (result < 0) { 27 | break; 28 | } 29 | result = init_audio_filter(volume_factor); 30 | if (result < 0) { 31 | break; 32 | } 33 | result = audio_filtering(); 34 | if (result < 0) { 35 | break; 36 | } 37 | } while (0); 38 | 39 | destroy_audio_filter(); 40 | close_input_output_files(); 41 | return result; 42 | } 43 | -------------------------------------------------------------------------------- /chapter14/video_filter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "io_data.h" 6 | #include "video_filter_core.h" 7 | 8 | static void usage(const char *program_name) { 9 | std::cout << "usage: " << std::string(program_name) 10 | << " input_file pic_width pic_height pix_fmt filter_discr" 11 | << std::endl; 12 | } 13 | 14 | int main(int argc, char **argv) { 15 | if (argc < 6) { 16 | usage(argv[0]); 17 | return 1; 18 | } 19 | char *input_file_name = argv[1]; 20 | int32_t pic_width = atoi(argv[2]); 21 | int32_t pic_height = atoi(argv[3]); 22 | int32_t total_frame_cnt = atoi(argv[4]); 23 | char *filter_descr = argv[5]; 24 | char *output_file_name = argv[6]; 25 | 26 | int32_t result = open_input_output_files(input_file_name, output_file_name); 27 | if (result < 0) { 28 | return result; 29 | } 30 | 31 | result = init_video_filter(pic_width, pic_height, filter_descr); 32 | if (result < 0) { 33 | return result; 34 | } 35 | 36 | result = filtering_video(total_frame_cnt); 37 | if (result < 0) { 38 | return result; 39 | } 40 | 41 | close_input_output_files(); 42 | destroy_video_filter(); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /chapter15/15-使用FFmpeg SDK进行视频图像转换与音频重采样.md: -------------------------------------------------------------------------------- 1 | ## 15-使用FFmpeg SDK进行视频图像转换与音频重采样 2 | 3 | 视频缩放和图像格式转换 4 | 音频信号的重采样 5 | 6 | ### 15.1 视频图像转换 7 | 8 | 可以对照源码中doc/examples/scaling_video.c 9 | 10 | 调用libswscale库,将YUV420P格式的图像转换为RGB24格式 11 | SwsContext 图像转换的核心结构 12 | sws_getContext 创建SwsContext结构指针 13 | sws_scale 图像转换 14 | 15 | ### 15.2 音频重采样 16 | 17 | 可以对照源码中doc/examples/resampling_audio.c 但不相同 18 | 19 | 调用libswresample库,对原音频信息进行冲采样 20 | SwrContext 音频信号重采样的核心结构 21 | swr_alloc 创建SwrContext结构指针 22 | swr_init 在设置好SwrContext参数后,初始化 23 | swr_convert 将按某采样率采样的一段输入音频信号按照指定采样率转换为输出音频信号 -------------------------------------------------------------------------------- /chapter15/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ##video_transformer 2 | add_executable(video_transformer video_transformer.cpp 3 | ../src/io_data.cpp 4 | ../src/video_swscale_core.cpp) 5 | target_link_libraries(video_transformer ${FFMPEG_LIBS} ${LINK_LIBS}) 6 | 7 | ##audio_resampler 8 | add_executable(audio_resampler audio_resampler.cpp 9 | ../src/io_data.cpp 10 | ../src/audio_resampler_core.cpp) 11 | target_link_libraries(audio_resampler ${FFMPEG_LIBS} ${LINK_LIBS}) -------------------------------------------------------------------------------- /chapter15/audio_resampler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "audio_resampler_core.h" 6 | #include "io_data.h" 7 | 8 | static void usage(const char *program_name) { 9 | std::cout << "usage: " << std::string(program_name) 10 | << " in_file in_sample_rate in_sample_fmt out_file out_sample_fmt " 11 | "out_sample_fmt " 12 | << std::endl; 13 | } 14 | 15 | int main(int argc, char **argv) { 16 | int result = 0; 17 | if (argc < 7) { 18 | usage(argv[0]); 19 | return -1; 20 | } 21 | 22 | char *input_file_name = argv[1]; 23 | int32_t in_sample_rate = atoi(argv[2]); 24 | char *in_sample_fmt = argv[3]; 25 | char *in_sample_layout = argv[4]; 26 | 27 | char *output_file_name = argv[5]; 28 | int32_t out_sample_rate = atoi(argv[6]); 29 | char *out_sample_fmt = argv[7]; 30 | char *out_sample_layout = argv[8]; 31 | 32 | do { 33 | result = open_input_output_files(input_file_name, output_file_name); 34 | if (result < 0) { 35 | break; 36 | } 37 | result = init_audio_resampler(in_sample_rate, in_sample_fmt, 38 | in_sample_layout, out_sample_rate, 39 | out_sample_fmt, out_sample_layout); 40 | if (result < 0) { 41 | std::cerr << "Error: init_audio_resampler failed." << std::endl; 42 | return result; 43 | } 44 | result = audio_resampling(); 45 | if (result < 0) { 46 | std::cerr << "Error: audio_resampling failed." << std::endl; 47 | return result; 48 | } 49 | } while (0); 50 | 51 | close_input_output_files(); 52 | destroy_audio_resampler(); 53 | return result; 54 | } 55 | -------------------------------------------------------------------------------- /chapter15/video_transformer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "io_data.h" 6 | #include "video_swscale_core.h" 7 | 8 | static void usage(const char *program_name) { 9 | std::cout << "usage: " << std::string(program_name) 10 | << " input_file input_size in_pix_fmt in_layout output_file " 11 | "output_size out_pix_fmt out_layout " 12 | << std::endl; 13 | } 14 | 15 | int main(int argc, char **argv) { 16 | int result = 0; 17 | if (argc < 7) { 18 | usage(argv[0]); 19 | return -1; 20 | } 21 | 22 | char *input_file_name = argv[1]; 23 | char *input_pic_size = argv[2]; 24 | char *input_pix_fmt = argv[3]; 25 | char *output_file_name = argv[4]; 26 | char *output_pic_size = argv[5]; 27 | char *output_pix_fmt = argv[6]; 28 | 29 | do { 30 | result = open_input_output_files(input_file_name, output_file_name); 31 | if (result < 0) { 32 | break; 33 | } 34 | result = init_video_swscale(input_pic_size, input_pix_fmt, output_pic_size, 35 | output_pix_fmt); 36 | if (result < 0) { 37 | break; 38 | } 39 | result = transforming(100); 40 | if (result < 0) { 41 | break; 42 | } 43 | } while (0); 44 | 45 | failed: 46 | destroy_video_swscale(); 47 | close_input_output_files(); 48 | return result; 49 | } 50 | -------------------------------------------------------------------------------- /inc/audio_decoder_core.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_DECODER_CORE_H 2 | #define AUDIO_DECODER_CORE_H 3 | 4 | #include 5 | 6 | int32_t init_audio_decoder(char *audio_codec_id); 7 | 8 | void destroy_audio_decoder(); 9 | 10 | int32_t audio_decoding(); 11 | 12 | #endif -------------------------------------------------------------------------------- /inc/audio_encoder_core.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_ENCODER_CORE_H 2 | #define AUDIO_ENCODER_CORE_H 3 | 4 | #include 5 | 6 | int32_t init_audio_encoder(const char *codec_name); 7 | 8 | int32_t audio_encoding(); 9 | 10 | void destroy_audio_encoder(); 11 | 12 | #endif -------------------------------------------------------------------------------- /inc/audio_filter_core.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_FILTER_CORE_H 2 | #define AUDIO_FILTER_CORE_H 3 | 4 | #include 5 | 6 | int32_t init_audio_filter(char *volume_factor); 7 | 8 | int32_t audio_filtering(); 9 | 10 | void destroy_audio_filter(); 11 | 12 | #endif -------------------------------------------------------------------------------- /inc/audio_resampler_core.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_RESAMPLER_CORE_H 2 | #define AUDIO_RESAMPLER_CORE_H 3 | 4 | #include 5 | 6 | int32_t init_audio_resampler(int32_t in_sample_rate, const char *in_sample_fmt, 7 | const char *in_ch_layout, int32_t out_sample_rate, 8 | const char *out_sample_fmt, 9 | const char *out_ch_layout); 10 | 11 | int32_t audio_resampling(); 12 | 13 | void destroy_audio_resampler(); 14 | 15 | #endif -------------------------------------------------------------------------------- /inc/demuxer_core.h: -------------------------------------------------------------------------------- 1 | #ifndef DEMXUER_CORE_H 2 | #define DEMXUER_CORE_H 3 | 4 | #include 5 | 6 | int32_t init_demuxer(char *input_name, char *video_output, char *audio_output); 7 | 8 | int32_t demuxing(char *video_output_name, char *audio_output_name); 9 | 10 | void destroy_demuxer(); 11 | 12 | #endif -------------------------------------------------------------------------------- /inc/io_data.h: -------------------------------------------------------------------------------- 1 | // io_data.h 2 | #ifndef IO_DATA_H 3 | #define IO_DATA_H 4 | extern "C" { 5 | #include 6 | } 7 | 8 | #include 9 | 10 | int32_t open_input_output_files(const char *input_name, 11 | const char *output_name); 12 | 13 | void close_input_output_files(); 14 | 15 | int32_t end_of_input_file(); 16 | 17 | int32_t read_data_to_buf(uint8_t *buf, int32_t size, int32_t &out_size); 18 | 19 | int32_t write_frame_to_yuv(AVFrame *frame); 20 | 21 | int32_t read_yuv_to_frame(AVFrame *frame); 22 | 23 | void write_pkt_to_file(AVPacket *pkt); 24 | 25 | int32_t write_samples_to_pcm(AVFrame *frame, AVCodecContext *codec_ctx); 26 | 27 | int32_t read_pcm_to_frame(AVFrame *frame, AVCodecContext *codec_ctx); 28 | 29 | int32_t write_samples_to_pcm2(AVFrame *frame, enum AVSampleFormat format, 30 | int channels); 31 | 32 | int32_t read_pcm_to_frame2(AVFrame *frame, enum AVSampleFormat format, 33 | int channels); 34 | 35 | void write_packed_data_to_file(const uint8_t *buf, int32_t size); 36 | 37 | #endif -------------------------------------------------------------------------------- /inc/muxer_core.h: -------------------------------------------------------------------------------- 1 | #ifndef MUXER_CORE_H 2 | #define MUXER_CORE_H 3 | 4 | #include 5 | 6 | int32_t init_muxer(char *video_input_file, char *audio_input_file, 7 | char *output_file); 8 | 9 | int32_t muxing(); 10 | 11 | void destroy_muxer(); 12 | 13 | #endif -------------------------------------------------------------------------------- /inc/video_decoder_core.h: -------------------------------------------------------------------------------- 1 | // video_encoder_core.h 2 | #ifndef VIDEO_DECODER_CORE_H 3 | #define VIDEO_DECODER_CORE_H 4 | 5 | #include 6 | 7 | int32_t init_video_decoder(); 8 | 9 | void destroy_video_decoder(); 10 | 11 | int32_t decoding(); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /inc/video_encoder_core.h: -------------------------------------------------------------------------------- 1 | // video_encoder_core.h 2 | #ifndef VIDEO_ENCODER_CORE_H 3 | #define VIDEO_ENCODER_CORE_H 4 | 5 | #include 6 | 7 | // 初始化视频编码器 8 | int32_t init_video_encoder(const char *codec_name); 9 | 10 | // 销毁视频编码器 11 | void destroy_video_encoder(); 12 | 13 | // 循环编码 14 | int32_t encoding(int32_t frame_cnt); 15 | 16 | #endif -------------------------------------------------------------------------------- /inc/video_filter_core.h: -------------------------------------------------------------------------------- 1 | #ifndef VIDEO_FILTER_CORE_H 2 | #define VIDEO_FILTER_CORE_H 3 | 4 | #include 5 | 6 | int32_t init_video_filter(int32_t width, int32_t height, 7 | const char *filter_descr); 8 | 9 | int32_t filtering_video(int32_t frame_cnt); 10 | 11 | void destroy_video_filter(); 12 | 13 | #endif -------------------------------------------------------------------------------- /inc/video_swscale_core.h: -------------------------------------------------------------------------------- 1 | #ifndef VIDEO_SWSCALE_CORE_H 2 | #define VIDEO_SWSCALE_CORE_H 3 | 4 | #include 5 | 6 | int32_t init_video_swscale(char *src_size, char *src_fmt, char *dst_size, 7 | char *dst_fmt); 8 | 9 | int32_t transforming(int32_t frame_cnt); 10 | 11 | void destroy_video_swscale(); 12 | 13 | #endif -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << "Hello, World!" << std::endl; 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /src/audio_decoder_core.cpp: -------------------------------------------------------------------------------- 1 | #include "audio_decoder_core.h" 2 | 3 | extern "C" { 4 | #include 5 | } 6 | 7 | #include 8 | 9 | #include "io_data.h" 10 | 11 | #define AUDIO_INBUF_SIZE 20480 12 | #define AUDIO_REFILL_THRESH 4096 13 | 14 | static AVCodec *codec = nullptr; 15 | static AVCodecContext *codec_ctx = nullptr; 16 | static AVCodecParserContext *parser = nullptr; 17 | 18 | static AVFrame *frame = nullptr; 19 | static AVPacket *pkt = nullptr; 20 | static enum AVCodecID audio_codec_id; 21 | 22 | int32_t init_audio_decoder(char *audio_codec) { 23 | if (strcasecmp(audio_codec, "MP3") == 0) { 24 | audio_codec_id = AV_CODEC_ID_MP3; 25 | std::cout << "Select codec id: MP3" << std::endl; 26 | } else if (strcasecmp(audio_codec, "AAC") == 0) { 27 | audio_codec_id = AV_CODEC_ID_AAC; 28 | std::cout << "Select codec id: AAC" << std::endl; 29 | } else { 30 | std::cerr << "Error invalid audio format." << std::endl; 31 | return -1; 32 | } 33 | codec = avcodec_find_decoder(audio_codec_id); 34 | if (!codec) { 35 | std::cerr << "Error: could not find codec." << std::endl; 36 | return -1; 37 | } 38 | parser = av_parser_init(codec->id); 39 | if (!parser) { 40 | std::cerr << "Error: could not init parser." << std::endl; 41 | return -1; 42 | } 43 | codec_ctx = avcodec_alloc_context3(codec); 44 | if (!codec_ctx) { 45 | std::cerr << "Error: could not alloc codec." << std::endl; 46 | return -1; 47 | } 48 | int32_t result = avcodec_open2(codec_ctx, codec, nullptr); 49 | if (result < 0) { 50 | std::cerr << "Error: could not open codec." << std::endl; 51 | return -1; 52 | } 53 | frame = av_frame_alloc(); 54 | if (!frame) { 55 | std::cerr << "Error: could not alloc frame." << std::endl; 56 | return -1; 57 | } 58 | pkt = av_packet_alloc(); 59 | if (!pkt) { 60 | std::cerr << "Error: could not alloc packet." << std::endl; 61 | return -1; 62 | } 63 | return 0; 64 | } 65 | 66 | void destroy_audio_decoder() { 67 | av_parser_close(parser); 68 | avcodec_free_context(&codec_ctx); 69 | av_frame_free(&frame); 70 | av_packet_free(&pkt); 71 | } 72 | 73 | static int32_t decode_packet(bool flushing) { 74 | int32_t result = 0; 75 | result = avcodec_send_packet(codec_ctx, flushing ? nullptr : pkt); 76 | if (result < 0) { 77 | std::cerr << "Error: faile to send packet, result:" << result << std::endl; 78 | return -1; 79 | } 80 | while (result >= 0) { 81 | result = avcodec_receive_frame(codec_ctx, frame); 82 | if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) 83 | return 1; 84 | else if (result < 0) { 85 | std::cerr << "Error: faile to receive frame, result:" << result 86 | << std::endl; 87 | return -1; 88 | } 89 | if (flushing) { 90 | std::cout << "Flushing:"; 91 | } 92 | write_samples_to_pcm(frame, codec_ctx); 93 | std::cout << "frame->nb_samples:" << frame->nb_samples 94 | << ", frame->channels:" << frame->channels << std::endl; 95 | } 96 | return result; 97 | } 98 | 99 | static int get_format_from_sample_fmt(const char **fmt, 100 | enum AVSampleFormat sample_fmt) { 101 | int i; 102 | struct sample_fmt_entry { 103 | enum AVSampleFormat sample_fmt; 104 | const char *fmt_be, *fmt_le; 105 | } sample_fmt_entries[] = { 106 | {AV_SAMPLE_FMT_U8, "u8", "u8"}, 107 | {AV_SAMPLE_FMT_S16, "s16be", "s16le"}, 108 | {AV_SAMPLE_FMT_S32, "s32be", "s32le"}, 109 | {AV_SAMPLE_FMT_FLT, "f32be", "f32le"}, 110 | {AV_SAMPLE_FMT_DBL, "f64be", "f64le"}, 111 | }; 112 | *fmt = NULL; 113 | 114 | for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) { 115 | struct sample_fmt_entry *entry = &sample_fmt_entries[i]; 116 | if (sample_fmt == entry->sample_fmt) { 117 | *fmt = AV_NE(entry->fmt_be, entry->fmt_le); 118 | return 0; 119 | } 120 | } 121 | 122 | std::cerr << "sample format %s is not supported as output format\n" 123 | << av_get_sample_fmt_name(sample_fmt) << std::endl; 124 | return -1; 125 | } 126 | 127 | int32_t get_audio_format(AVCodecContext *codec_ctx) { 128 | int ret = 0; 129 | const char *fmt; 130 | enum AVSampleFormat sfmt = codec_ctx->sample_fmt; 131 | if (av_sample_fmt_is_planar(sfmt)) { 132 | const char *packed = av_get_sample_fmt_name(sfmt); 133 | std::cout << "Warning: the sample format the decoder produced is planar " 134 | << std::string(packed) 135 | << ", This example will output the first channel only." 136 | << std::endl; 137 | sfmt = av_get_packed_sample_fmt(sfmt); 138 | } 139 | 140 | int n_channels = codec_ctx->channels; 141 | if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0) { 142 | return -1; 143 | } 144 | 145 | std::cout << "Play command: ffpay -f " << std::string(fmt) << " -ac " 146 | << n_channels << " -ar " << codec_ctx->sample_rate << " output.pcm" 147 | << std::endl; 148 | return 0; 149 | } 150 | 151 | int32_t audio_decoding() { 152 | uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE] = {0}; 153 | int32_t result = 0; 154 | uint8_t *data = nullptr; 155 | int32_t data_size = 0; 156 | while (!end_of_input_file()) { 157 | result = read_data_to_buf(inbuf, AUDIO_INBUF_SIZE, data_size); 158 | if (result < 0) { 159 | std::cerr << "Error: read_data_to_buf failed." << std::endl; 160 | return -1; 161 | } 162 | 163 | data = inbuf; 164 | while (data_size > 0) { 165 | result = av_parser_parse2(parser, codec_ctx, &pkt->data, &pkt->size, data, 166 | data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); 167 | if (result < 0) { 168 | std::cerr << "Error: av_parser_parse2 failed." << std::endl; 169 | return -1; 170 | } 171 | 172 | data += result; 173 | data_size -= result; 174 | if (pkt->size) { 175 | std::cout << "Parsed packet size:" << pkt->size << std::endl; 176 | decode_packet(false); 177 | } 178 | } 179 | } 180 | decode_packet(true); 181 | get_audio_format(codec_ctx); 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /src/audio_encoder_core.cpp: -------------------------------------------------------------------------------- 1 | #include "audio_encoder_core.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | extern "C" { 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | } 15 | 16 | #include "io_data.h" 17 | 18 | static AVCodec *codec = nullptr; 19 | static AVCodecContext *codec_ctx = nullptr; 20 | static AVFrame *frame = nullptr; 21 | static AVPacket *pkt = nullptr; 22 | 23 | static enum AVCodecID audio_codec_id; 24 | 25 | int32_t init_audio_encoder(const char *codec_name) { 26 | if (strcasecmp(codec_name, "MP3") == 0) { 27 | audio_codec_id = AV_CODEC_ID_MP3; 28 | std::cout << "Select codec id: MP3" << std::endl; 29 | } else if (strcasecmp(codec_name, "AAC") == 0) { 30 | audio_codec_id = AV_CODEC_ID_AAC; 31 | std::cout << "Select codec id: AAC" << std::endl; 32 | } else { 33 | std::cerr << "Error invalid audio format." << std::endl; 34 | return -1; 35 | } 36 | 37 | codec = avcodec_find_encoder(audio_codec_id); 38 | if (!codec) { 39 | std::cerr << "Error: could not find codec." << std::endl; 40 | return -1; 41 | } 42 | 43 | codec_ctx = avcodec_alloc_context3(codec); 44 | if (!codec_ctx) { 45 | std::cerr << "Error: could not alloc codec." << std::endl; 46 | return -1; 47 | } 48 | 49 | // 设置音频编码器的参数 50 | codec_ctx->bit_rate = 128000; // 设置输出码率为128Kbps 51 | codec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; // 音频采样格式为fltp 52 | codec_ctx->sample_rate = 44100; // 音频采样率为44.1kHz 53 | codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO; // 声道布局为立体声 54 | codec_ctx->channels = 2; // 声道数为双声道 55 | 56 | int32_t result = avcodec_open2(codec_ctx, codec, nullptr); 57 | if (result < 0) { 58 | std::cerr << "Error: could not open codec." << std::endl; 59 | return -1; 60 | } 61 | 62 | frame = av_frame_alloc(); 63 | if (!frame) { 64 | std::cerr << "Error: could not alloc frame." << std::endl; 65 | return -1; 66 | } 67 | 68 | frame->nb_samples = codec_ctx->frame_size; 69 | frame->format = codec_ctx->sample_fmt; 70 | frame->channel_layout = codec_ctx->channel_layout; 71 | result = av_frame_get_buffer(frame, 0); 72 | if (result < 0) { 73 | std::cerr << "Error: AVFrame could not get buffer." << std::endl; 74 | return -1; 75 | } 76 | 77 | pkt = av_packet_alloc(); 78 | if (!pkt) { 79 | std::cerr << "Error: could not alloc packet." << std::endl; 80 | return -1; 81 | } 82 | return 0; 83 | } 84 | 85 | static int32_t encode_frame(bool flushing) { 86 | int32_t result = 0; 87 | result = avcodec_send_frame(codec_ctx, flushing ? nullptr : frame); 88 | if (result < 0) { 89 | std::cerr << "Error: avcodec_send_frame failed." << std::endl; 90 | return result; 91 | } 92 | 93 | while (result >= 0) { 94 | result = avcodec_receive_packet(codec_ctx, pkt); 95 | if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) { 96 | return 1; 97 | } else if (result < 0) { 98 | std::cerr << "Error: avcodec_receive_packet failed." << std::endl; 99 | return result; 100 | } 101 | std::cout << "received pkt size:" << pkt->size << std::endl; 102 | write_pkt_to_file(pkt); 103 | } 104 | return 0; 105 | } 106 | 107 | int32_t audio_encoding() { 108 | int32_t result = 0; 109 | while (!end_of_input_file()) { 110 | result = read_pcm_to_frame(frame, codec_ctx); 111 | if (result < 0) { 112 | std::cerr << "Error: read_pcm_to_frame failed." << std::endl; 113 | return -1; 114 | } 115 | 116 | result = encode_frame(false); 117 | if (result < 0) { 118 | std::cerr << "Error: encode_frame failed." << std::endl; 119 | return result; 120 | } 121 | } 122 | result = encode_frame(true); 123 | if (result < 0) { 124 | std::cerr << "Error: flushing failed." << std::endl; 125 | return result; 126 | } 127 | return 0; 128 | } 129 | 130 | void destroy_audio_encoder() { 131 | av_frame_free(&frame); 132 | av_packet_free(&pkt); 133 | avcodec_free_context(&codec_ctx); 134 | } 135 | -------------------------------------------------------------------------------- /src/audio_filter_core.cpp: -------------------------------------------------------------------------------- 1 | #include "audio_filter_core.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "audio_filter_core.h" 9 | #include "io_data.h" 10 | 11 | extern "C" { 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "libavfilter/avfilter.h" 18 | #include "libavutil/channel_layout.h" 19 | #include "libavutil/mem.h" 20 | #include "libavutil/opt.h" 21 | #include "libavutil/samplefmt.h" 22 | } 23 | 24 | #define INPUT_SAMPLERATE 44100 25 | #define INPUT_FORMAT AV_SAMPLE_FMT_FLTP 26 | #define INPUT_CHANNEL_LAYOUT AV_CH_LAYOUT_STEREO 27 | #define FRAME_SIZE 4096 28 | 29 | static AVFilterGraph *filter_graph; 30 | static AVFilterContext *abuffersrc_ctx; 31 | static AVFilterContext *volume_ctx; 32 | static AVFilterContext *aformat_ctx; 33 | static AVFilterContext *abuffersink_ctx; 34 | 35 | static AVFrame *input_frame = nullptr, *output_frame = nullptr; 36 | 37 | int32_t init_audio_filter(char *volume_factor) { 38 | int32_t result = 0; 39 | char ch_layout[64]; 40 | char options_str[1024]; 41 | AVDictionary *options_dict = NULL; 42 | 43 | /* 创建滤镜图 */ 44 | filter_graph = avfilter_graph_alloc(); 45 | if (!filter_graph) { 46 | std::cout << "Error: Unable to create filter graph." << std::endl; 47 | return AVERROR(ENOMEM); 48 | } 49 | 50 | /* 创建abuffer滤镜 */ 51 | const AVFilter *abuffer = avfilter_get_by_name("abuffer"); 52 | if (!abuffer) { 53 | std::cout << "Error: Could not find the abuffer filter." << std::endl; 54 | return AVERROR_FILTER_NOT_FOUND; 55 | } 56 | 57 | abuffersrc_ctx = avfilter_graph_alloc_filter(filter_graph, abuffer, "src"); 58 | if (!abuffersrc_ctx) { 59 | std::cout << "Error: Could not allocate the abuffer instance." << std::endl; 60 | return AVERROR(ENOMEM); 61 | } 62 | 63 | av_get_channel_layout_string(ch_layout, sizeof(ch_layout), 0, 64 | INPUT_CHANNEL_LAYOUT); 65 | av_opt_set(abuffersrc_ctx, "channel_layout", ch_layout, 66 | AV_OPT_SEARCH_CHILDREN); 67 | av_opt_set(abuffersrc_ctx, "sample_fmt", av_get_sample_fmt_name(INPUT_FORMAT), 68 | AV_OPT_SEARCH_CHILDREN); 69 | av_opt_set_q(abuffersrc_ctx, "time_base", (AVRational) {1, INPUT_SAMPLERATE}, 70 | AV_OPT_SEARCH_CHILDREN); 71 | av_opt_set_int(abuffersrc_ctx, "sample_rate", INPUT_SAMPLERATE, 72 | AV_OPT_SEARCH_CHILDREN); 73 | 74 | result = avfilter_init_str(abuffersrc_ctx, NULL); 75 | if (result < 0) { 76 | std::cout << "Error: Could not initialize the abuffer filter." << std::endl; 77 | return result; 78 | } 79 | 80 | /* 创建volumn滤镜 */ 81 | const AVFilter *volume = avfilter_get_by_name("volume"); 82 | if (!volume) { 83 | std::cout << "Error: Could not find the volumn filter." << std::endl; 84 | return AVERROR_FILTER_NOT_FOUND; 85 | } 86 | 87 | volume_ctx = avfilter_graph_alloc_filter(filter_graph, volume, "volume"); 88 | if (!volume_ctx) { 89 | std::cout << "Error: Could not allocate the volume instance." << std::endl; 90 | return AVERROR(ENOMEM); 91 | } 92 | 93 | av_dict_set(&options_dict, "volume", volume_factor, 0); 94 | result = avfilter_init_dict(volume_ctx, &options_dict); 95 | av_dict_free(&options_dict); 96 | if (result < 0) { 97 | std::cout << "Error: Could not initialize the volume filter." << std::endl; 98 | return result; 99 | } 100 | 101 | /* 创建aformat滤镜 */ 102 | const AVFilter *aformat = avfilter_get_by_name("aformat"); 103 | if (!aformat) { 104 | std::cout << "Error: Could not find the aformat filter." << std::endl; 105 | return AVERROR_FILTER_NOT_FOUND; 106 | } 107 | 108 | aformat_ctx = avfilter_graph_alloc_filter(filter_graph, aformat, "aformat"); 109 | if (!aformat_ctx) { 110 | std::cout << "Error: Could not allocate the aformat instance." << std::endl; 111 | return AVERROR(ENOMEM); 112 | } 113 | 114 | snprintf(options_str, sizeof(options_str), 115 | "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64, 116 | av_get_sample_fmt_name(AV_SAMPLE_FMT_S16), 22050, 117 | (uint64_t) AV_CH_LAYOUT_MONO); 118 | result = avfilter_init_str(aformat_ctx, options_str); 119 | if (result < 0) { 120 | std::cout << "Error: Could not initialize the aformat filter." << std::endl; 121 | return result; 122 | } 123 | 124 | /* 创建abuffersink滤镜 */ 125 | const AVFilter *abuffersink = avfilter_get_by_name("abuffersink"); 126 | if (!abuffersink) { 127 | std::cout << "Error: Could not find the abuffersink filter." << std::endl; 128 | return AVERROR_FILTER_NOT_FOUND; 129 | } 130 | 131 | abuffersink_ctx = 132 | avfilter_graph_alloc_filter(filter_graph, abuffersink, "sink"); 133 | if (!abuffersink_ctx) { 134 | std::cout << "Error: Could not allocate the abuffersink instance." 135 | << std::endl; 136 | return AVERROR(ENOMEM); 137 | } 138 | 139 | result = avfilter_init_str(abuffersink_ctx, NULL); 140 | if (result < 0) { 141 | std::cout << "Error: Could not initialize the abuffersink instance." 142 | << std::endl; 143 | return result; 144 | } 145 | 146 | /* 连接创建好的滤镜 */ 147 | result = avfilter_link(abuffersrc_ctx, 0, volume_ctx, 0); 148 | if (result >= 0) result = avfilter_link(volume_ctx, 0, aformat_ctx, 0); 149 | if (result >= 0) result = avfilter_link(aformat_ctx, 0, abuffersink_ctx, 0); 150 | if (result < 0) { 151 | fprintf(stderr, "Error connecting filters\n"); 152 | return result; 153 | } 154 | 155 | /* 配置滤镜图 */ 156 | result = avfilter_graph_config(filter_graph, NULL); 157 | if (result < 0) { 158 | std::cout << "Error: Error configuring the filter graph." << std::endl; 159 | return result; 160 | } 161 | 162 | /* 创建输入帧对象和输出帧对象 */ 163 | input_frame = av_frame_alloc(); 164 | if (!input_frame) { 165 | std::cerr << "Error: could not alloc input frame." << std::endl; 166 | return -1; 167 | } 168 | 169 | output_frame = av_frame_alloc(); 170 | if (!output_frame) { 171 | std::cerr << "Error: could not alloc input frame." << std::endl; 172 | return -1; 173 | } 174 | 175 | return result; 176 | } 177 | 178 | static int32_t filter_frame() { 179 | int32_t result = av_buffersrc_add_frame(abuffersrc_ctx, input_frame); 180 | if (result < 0) { 181 | std::cerr << "Error:add frame to buffersrc failed." << std::endl; 182 | return result; 183 | } 184 | 185 | while (1) { 186 | result = av_buffersink_get_frame(abuffersink_ctx, output_frame); 187 | if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) { 188 | return 1; 189 | } else if (result < 0) { 190 | std::cerr << "Error: buffersink_get_frame failed." << std::endl; 191 | return result; 192 | } 193 | std::cout << "Output channels:" << output_frame->channels 194 | << ", nb_samples : " << output_frame->nb_samples 195 | << ", sample_fmt : " << output_frame->format << std::endl; 196 | write_samples_to_pcm2(output_frame, (AVSampleFormat) output_frame->format, 197 | output_frame->channels); 198 | av_frame_unref(output_frame); 199 | } 200 | 201 | return result; 202 | } 203 | 204 | static int32_t init_frame() { 205 | input_frame->sample_rate = INPUT_SAMPLERATE; 206 | input_frame->nb_samples = FRAME_SIZE; 207 | input_frame->format = INPUT_FORMAT; 208 | input_frame->channel_layout = INPUT_CHANNEL_LAYOUT; 209 | input_frame->channels = 210 | av_get_channel_layout_nb_channels(INPUT_CHANNEL_LAYOUT); 211 | int32_t result = av_frame_get_buffer(input_frame, 0); 212 | if (result < 0) { 213 | std::cerr << "Error: AVFrame could not get buffer." << std::endl; 214 | return -1; 215 | } 216 | return 0; 217 | } 218 | 219 | int32_t audio_filtering() { 220 | int32_t result = 0; 221 | while (!end_of_input_file()) { 222 | result = init_frame(); 223 | if (result < 0) { 224 | std::cerr << "Error: init_frame failed." << std::endl; 225 | return result; 226 | } 227 | result = read_pcm_to_frame2(input_frame, INPUT_FORMAT, 2); 228 | if (result < 0) { 229 | std::cerr << "Error: read_pcm_to_frame failed." << std::endl; 230 | return -1; 231 | } 232 | result = filter_frame(); 233 | if (result < 0) { 234 | std::cerr << "Error: filter_frame failed." << std::endl; 235 | return -1; 236 | } 237 | } 238 | return result; 239 | } 240 | 241 | static void free_frames() { 242 | av_frame_free(&input_frame); 243 | av_frame_free(&output_frame); 244 | } 245 | 246 | void destroy_audio_filter() { 247 | free_frames(); 248 | avfilter_graph_free(&filter_graph); 249 | } 250 | -------------------------------------------------------------------------------- /src/audio_resampler_core.cpp: -------------------------------------------------------------------------------- 1 | #include "audio_resampler_core.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "io_data.h" 9 | 10 | extern "C" { 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | } 17 | 18 | #define SRC_NB_SAMPLES 1152 19 | 20 | static struct SwrContext *swr_ctx; 21 | static AVFrame *input_frame = nullptr; 22 | int32_t dst_nb_samples, max_dst_nb_samples, dst_nb_channels, dst_rate, src_rate; 23 | enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_NONE, 24 | dst_sample_fmt = AV_SAMPLE_FMT_NONE; 25 | uint8_t **dst_data = NULL; 26 | int32_t dst_linesize = 0; 27 | 28 | static int32_t init_frame(int sample_rate, int sample_format, 29 | uint64_t channel_layout) { 30 | int32_t result = 0; 31 | input_frame->sample_rate = sample_rate; 32 | input_frame->nb_samples = SRC_NB_SAMPLES; 33 | input_frame->format = sample_format; 34 | input_frame->channel_layout = channel_layout; 35 | 36 | result = av_frame_get_buffer(input_frame, 0); 37 | if (result < 0) { 38 | std::cerr << "Error: AVFrame could not get buffer." << std::endl; 39 | return -1; 40 | } 41 | 42 | return result; 43 | } 44 | 45 | int32_t init_audio_resampler(int32_t in_sample_rate, const char *in_sample_fmt, 46 | const char *in_ch_layout, int32_t out_sample_rate, 47 | const char *out_sample_fmt, 48 | const char *out_ch_layout) { 49 | int32_t result = 0; 50 | swr_ctx = swr_alloc(); 51 | if (!swr_ctx) { 52 | std::cerr << "Error: failed to allocate SwrContext." << std::endl; 53 | return -1; 54 | } 55 | 56 | int64_t src_ch_layout = -1, dst_ch_layout = -1; 57 | if (!strcasecmp(in_ch_layout, "MONO")) { 58 | src_ch_layout = AV_CH_LAYOUT_MONO; 59 | } else if (!strcasecmp(in_ch_layout, "STEREO")) { 60 | src_ch_layout = AV_CH_LAYOUT_STEREO; 61 | } else if (!strcasecmp(in_ch_layout, "SURROUND")) { 62 | src_ch_layout = AV_CH_LAYOUT_SURROUND; 63 | } else { 64 | std::cerr << "Error: unsupported input channel layout." << std::endl; 65 | return -1; 66 | } 67 | if (!strcasecmp(out_ch_layout, "MONO")) { 68 | dst_ch_layout = AV_CH_LAYOUT_MONO; 69 | } else if (!strcasecmp(out_ch_layout, "STEREO")) { 70 | dst_ch_layout = AV_CH_LAYOUT_STEREO; 71 | } else if (!strcasecmp(out_ch_layout, "SURROUND")) { 72 | dst_ch_layout = AV_CH_LAYOUT_SURROUND; 73 | } else { 74 | std::cerr << "Error: unsupported output channel layout." << std::endl; 75 | return -1; 76 | } 77 | 78 | if (!strcasecmp(in_sample_fmt, "fltp")) { 79 | src_sample_fmt = AV_SAMPLE_FMT_FLTP; 80 | } else if (!strcasecmp(in_sample_fmt, "s16")) { 81 | src_sample_fmt = AV_SAMPLE_FMT_S16P; 82 | } else { 83 | std::cerr << "Error: unsupported input sample format." << std::endl; 84 | return -1; 85 | } 86 | if (!strcasecmp(out_sample_fmt, "fltp")) { 87 | dst_sample_fmt = AV_SAMPLE_FMT_FLTP; 88 | } else if (!strcasecmp(out_sample_fmt, "s16")) { 89 | dst_sample_fmt = AV_SAMPLE_FMT_S16P; 90 | } else { 91 | std::cerr << "Error: unsupported output sample format." << std::endl; 92 | return -1; 93 | } 94 | 95 | src_rate = in_sample_rate; 96 | dst_rate = out_sample_rate; 97 | av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0); 98 | av_opt_set_int(swr_ctx, "in_sample_rate", src_rate, 0); 99 | av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_sample_fmt, 0); 100 | 101 | av_opt_set_int(swr_ctx, "out_channel_layout", dst_ch_layout, 0); 102 | av_opt_set_int(swr_ctx, "out_sample_rate", dst_rate, 0); 103 | av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0); 104 | 105 | result = swr_init(swr_ctx); 106 | if (result < 0) { 107 | std::cerr << "Error: failed to initialize SwrContext." << std::endl; 108 | return -1; 109 | } 110 | 111 | input_frame = av_frame_alloc(); 112 | if (!input_frame) { 113 | std::cerr << "Error: could not alloc input frame." << std::endl; 114 | return -1; 115 | } 116 | result = init_frame(in_sample_rate, src_sample_fmt, src_ch_layout); 117 | if (result < 0) { 118 | std::cerr << "Error: failed to initialize input frame." << std::endl; 119 | return -1; 120 | } 121 | max_dst_nb_samples = dst_nb_samples = av_rescale_rnd( 122 | SRC_NB_SAMPLES, out_sample_rate, in_sample_rate, AV_ROUND_UP); 123 | dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout); 124 | std::cout << "max_dst_nb_samples:" << max_dst_nb_samples 125 | << ", dst_nb_channels : " << dst_nb_channels << std::endl; 126 | 127 | return result; 128 | } 129 | 130 | static int32_t resampling_frame() { 131 | int32_t result = 0; 132 | int32_t dst_bufsize = 0; 133 | dst_nb_samples = 134 | av_rescale_rnd(swr_get_delay(swr_ctx, src_rate) + SRC_NB_SAMPLES, 135 | dst_rate, src_rate, AV_ROUND_UP); 136 | if (dst_nb_samples > max_dst_nb_samples) { 137 | av_freep(&dst_data[0]); 138 | result = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels, 139 | dst_nb_samples, dst_sample_fmt, 1); 140 | if (result < 0) { 141 | std::cerr << "Error:failed to reallocat dst_data." << std::endl; 142 | return -1; 143 | } 144 | std::cout << "nb_samples exceeds max_dst_nb_samples, buffer reallocated." 145 | << std::endl; 146 | max_dst_nb_samples = dst_nb_samples; 147 | } 148 | result = swr_convert(swr_ctx, dst_data, dst_nb_samples, 149 | (const uint8_t **) input_frame->data, SRC_NB_SAMPLES); 150 | if (result < 0) { 151 | std::cerr << "Error:swr_convert failed." << std::endl; 152 | return -1; 153 | } 154 | dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels, 155 | result, dst_sample_fmt, 1); 156 | if (dst_bufsize < 0) { 157 | std::cerr << "Error:Could not get sample buffer size." << std::endl; 158 | return -1; 159 | } 160 | write_packed_data_to_file(dst_data[0], dst_bufsize); 161 | 162 | return result; 163 | } 164 | 165 | int32_t audio_resampling() { 166 | int32_t result = av_samples_alloc_array_and_samples( 167 | &dst_data, &dst_linesize, dst_nb_channels, dst_nb_samples, dst_sample_fmt, 168 | 0); 169 | if (result < 0) { 170 | std::cerr << "Error: av_samples_alloc_array_and_samples failed." 171 | << std::endl; 172 | return -1; 173 | } 174 | std::cout << "dst_linesize:" << dst_linesize << std::endl; 175 | 176 | while (!end_of_input_file()) { 177 | result = read_pcm_to_frame2(input_frame, src_sample_fmt, 2); 178 | if (result < 0) { 179 | std::cerr << "Error: read_pcm_to_frame failed." << std::endl; 180 | return -1; 181 | } 182 | result = resampling_frame(); 183 | if (result < 0) { 184 | std::cerr << "Error: resampling_frame failed." << std::endl; 185 | return -1; 186 | } 187 | } 188 | 189 | return result; 190 | } 191 | 192 | void destroy_audio_resampler() { 193 | av_frame_free(&input_frame); 194 | if (dst_data) av_freep(&dst_data[0]); 195 | av_freep(&dst_data); 196 | swr_free(&swr_ctx); 197 | } 198 | -------------------------------------------------------------------------------- /src/io_data.cpp: -------------------------------------------------------------------------------- 1 | // io_data.cpp 2 | #include "io_data.h" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | static FILE *input_file = nullptr; 10 | static FILE *output_file = nullptr; 11 | 12 | int32_t open_input_output_files(const char *input_name, 13 | const char *output_name) { 14 | if (strlen(input_name) == 0 || strlen(output_name) == 0) { 15 | std::cerr << "Error: empty input or output file name." << std::endl; 16 | return -1; 17 | } 18 | 19 | close_input_output_files(); 20 | 21 | input_file = fopen(input_name, "rb"); 22 | if (input_file == nullptr) { 23 | std::cerr << "Error: failed to open input file." << std::endl; 24 | return -1; 25 | } 26 | output_file = fopen(output_name, "wb"); 27 | if (output_file == nullptr) { 28 | std::cerr << "Error: failed to open output file." << std::endl; 29 | return -1; 30 | } 31 | 32 | return 0; 33 | } 34 | 35 | void close_input_output_files() { 36 | if (input_file != nullptr) { 37 | fclose(input_file); 38 | input_file = nullptr; 39 | } 40 | if (output_file != nullptr) { 41 | fclose(output_file); 42 | output_file = nullptr; 43 | } 44 | } 45 | 46 | int32_t end_of_input_file() { return feof(input_file); } 47 | 48 | int32_t read_data_to_buf(uint8_t *buf, int32_t size, int32_t &out_size) { 49 | int32_t read_size = fread(buf, 1, size, input_file); 50 | if (read_size == 0) { 51 | std::cerr << "Error: read_data_to_buf failed." << std::endl; 52 | return -1; 53 | } 54 | out_size = read_size; 55 | return 0; 56 | } 57 | 58 | int32_t write_frame_to_yuv(AVFrame *frame) { 59 | uint8_t **pBuf = frame->data; 60 | int *pStride = frame->linesize; 61 | for (size_t i = 0; i < 3; i++) { 62 | int32_t width = (i == 0 ? frame->width : frame->width / 2); 63 | int32_t height = (i == 0 ? frame->height : frame->height / 2); 64 | for (size_t j = 0; j < height; j++) { 65 | fwrite(pBuf[i], 1, width, output_file); 66 | pBuf[i] += pStride[i]; 67 | } 68 | } 69 | return 0; 70 | } 71 | 72 | int32_t read_yuv_to_frame(AVFrame *frame) { 73 | int32_t frame_width = frame->width; 74 | int32_t frame_height = frame->height; 75 | int32_t luma_stride = frame->linesize[0]; 76 | int32_t chroma_stride = frame->linesize[1]; 77 | int32_t frame_size = frame_width * frame_height * 3 / 2; 78 | int32_t read_size = 0; 79 | 80 | if (frame_width == luma_stride) { 81 | // 如果 width 等于 stride,说明 frame 中不存在 padding 82 | // 字节,可向其中整体读取 83 | read_size += 84 | fread(frame->data[0], 1, frame_width * frame_height, input_file); 85 | read_size += 86 | fread(frame->data[1], 1, frame_width * frame_height / 4, input_file); 87 | read_size += 88 | fread(frame->data[2], 1, frame_width * frame_height / 4, input_file); 89 | } else { 90 | // 如果 width 不等于 stride,说明 frame 一行的末尾存在 padding 91 | // 字节,三个分量都应当逐行读取 92 | for (size_t i = 0; i < frame_height; i++) { 93 | read_size += 94 | fread(frame->data[0] + i * luma_stride, 1, frame_width, input_file); 95 | } 96 | for (size_t uv = 1; uv < 2; uv++) { 97 | for (size_t i = 0; i < frame_height / 2; i++) { 98 | read_size += fread(frame->data[uv] + i * chroma_stride, 1, 99 | frame_width / 2, input_file); 100 | } 101 | } 102 | } 103 | 104 | // 验证读取数据正确 105 | if (read_size != frame_size) { 106 | std::cerr << "Error: Read data error, frame_size:" << frame_size 107 | << ", read_size:" << read_size << std::endl; 108 | return -1; 109 | } 110 | 111 | return 0; 112 | } 113 | 114 | void write_pkt_to_file(AVPacket *pkt) { 115 | fwrite(pkt->data, 1, pkt->size, output_file); 116 | } 117 | 118 | int32_t write_samples_to_pcm(AVFrame *frame, AVCodecContext *codec_ctx) { 119 | int data_size = av_get_bytes_per_sample(codec_ctx->sample_fmt); 120 | if (data_size < 0) { 121 | /* This should not occur, checking just for paranoia */ 122 | std::cerr << "Failed to calculate data size" << std::endl; 123 | exit(1); 124 | } 125 | for (int i = 0; i < frame->nb_samples; i++) { 126 | for (int ch = 0; ch < codec_ctx->channels; ch++) { 127 | fwrite(frame->data[ch] + data_size * i, 1, data_size, output_file); 128 | } 129 | } 130 | return 0; 131 | } 132 | 133 | int32_t read_pcm_to_frame(AVFrame *frame, AVCodecContext *codec_ctx) { 134 | int data_size = av_get_bytes_per_sample(codec_ctx->sample_fmt); 135 | if (data_size < 0) { 136 | /* This should not occur, checking just for paranoia */ 137 | std::cerr << "Failed to calculate data size" << std::endl; 138 | return -1; 139 | } 140 | 141 | // 从输入文件中交替读取一个采样值的各个声道的数据, 142 | // 保存到AVFrame结构的存储分量中 143 | for (int i = 0; i < frame->nb_samples; i++) { 144 | for (int ch = 0; ch < codec_ctx->channels; ch++) { 145 | fread(frame->data[ch] + data_size * i, 1, data_size, input_file); 146 | } 147 | } 148 | return 0; 149 | } 150 | 151 | int32_t write_samples_to_pcm2(AVFrame *frame, enum AVSampleFormat format, 152 | int channels) { 153 | int data_size = av_get_bytes_per_sample(format); 154 | if (data_size < 0) { 155 | /* This should not occur, checking just for paranoia */ 156 | std::cerr << "Failed to calculate data size" << std::endl; 157 | exit(1); 158 | } 159 | for (int i = 0; i < frame->nb_samples; i++) { 160 | for (int ch = 0; ch < channels; ch++) { 161 | fwrite(frame->data[ch] + data_size * i, 1, data_size, output_file); 162 | } 163 | } 164 | return 0; 165 | } 166 | 167 | int32_t read_pcm_to_frame2(AVFrame *frame, enum AVSampleFormat format, 168 | int channels) { 169 | int data_size = av_get_bytes_per_sample(format); 170 | if (data_size < 0) { 171 | /* This should not occur, checking just for paranoia */ 172 | std::cerr << "Failed to calculate data size" << std::endl; 173 | return -1; 174 | } 175 | 176 | // 从输入文件中交替读取一个采样值的各个声道的数据, 177 | // 保存到AVFrame结构的存储分量中 178 | for (int i = 0; i < frame->nb_samples; i++) { 179 | for (int ch = 0; ch < channels; ch++) { 180 | fread(frame->data[ch] + data_size * i, 1, data_size, input_file); 181 | } 182 | } 183 | return 0; 184 | } 185 | 186 | void write_packed_data_to_file(const uint8_t *buf, int32_t size) { 187 | fwrite(buf, 1, size, output_file); 188 | } -------------------------------------------------------------------------------- /src/video_decoder_core.cpp: -------------------------------------------------------------------------------- 1 | // video_encoder_core.cpp 2 | extern "C" { 3 | #include 4 | } 5 | 6 | #include 7 | 8 | #include "io_data.h" 9 | #include "video_decoder_core.h" 10 | 11 | #define INBUF_SIZE 4096 12 | 13 | static AVCodec *codec = nullptr; 14 | static AVCodecContext *codec_ctx = nullptr; 15 | 16 | static AVCodecParserContext *parser = nullptr; 17 | 18 | static AVFrame *frame = nullptr; 19 | static AVPacket *pkt = nullptr; 20 | 21 | int32_t init_video_decoder() { 22 | codec = avcodec_find_decoder(AV_CODEC_ID_H264); 23 | if (!codec) { 24 | std::cerr << "Error: could not find codec." << std::endl; 25 | return -1; 26 | } 27 | 28 | parser = av_parser_init(codec->id); 29 | if (!parser) { 30 | std::cerr << "Error: could not init parser." << std::endl; 31 | return -1; 32 | } 33 | 34 | codec_ctx = avcodec_alloc_context3(codec); 35 | if (!codec_ctx) { 36 | std::cerr << "Error: could not alloc codec." << std::endl; 37 | return -1; 38 | } 39 | 40 | int32_t result = avcodec_open2(codec_ctx, codec, nullptr); 41 | if (result < 0) { 42 | std::cerr << "Error: could not open codec." << std::endl; 43 | return -1; 44 | } 45 | 46 | frame = av_frame_alloc(); 47 | if (!frame) { 48 | std::cerr << "Error: could not alloc frame." << std::endl; 49 | return -1; 50 | } 51 | 52 | pkt = av_packet_alloc(); 53 | if (!pkt) { 54 | std::cerr << "Error: could not alloc packet." << std::endl; 55 | return -1; 56 | } 57 | 58 | return 0; 59 | } 60 | 61 | static int32_t decode_packet(bool flushing) { 62 | int32_t result = 0; 63 | result = avcodec_send_packet(codec_ctx, flushing ? nullptr : pkt); 64 | if (result < 0) { 65 | std::cerr << "Error: faile to send packet, result:" << result << std::endl; 66 | return -1; 67 | } 68 | 69 | while (result >= 0) { 70 | result = avcodec_receive_frame(codec_ctx, frame); 71 | if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) 72 | return 1; 73 | else if (result < 0) { 74 | std::cerr << "Error: faile to receive frame, result:" << result 75 | << std::endl; 76 | return -1; 77 | } 78 | if (flushing) { 79 | std::cout << "Flushing:"; 80 | } 81 | std::cout << "Write frame pic_num:" << frame->coded_picture_number 82 | << std::endl; 83 | write_frame_to_yuv(frame); 84 | } 85 | return 0; 86 | } 87 | 88 | int32_t decoding() { 89 | uint8_t inbuf[INBUF_SIZE] = {0}; 90 | int32_t result = 0; 91 | uint8_t *data = nullptr; 92 | int32_t data_size = 0; 93 | while (!end_of_input_file()) { 94 | result = read_data_to_buf(inbuf, INBUF_SIZE, data_size); 95 | if (result < 0) { 96 | std::cerr << "Error: read_data_to_buf failed." << std::endl; 97 | return -1; 98 | } 99 | 100 | data = inbuf; 101 | while (data_size > 0) { 102 | result = av_parser_parse2(parser, codec_ctx, &pkt->data, &pkt->size, data, 103 | data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); 104 | if (result < 0) { 105 | std::cerr << "Error: av_parser_parse2 failed." << std::endl; 106 | return -1; 107 | } 108 | 109 | data += result; 110 | data_size -= result; 111 | 112 | if (pkt->size) { 113 | std::cout << "Parsed packet size:" << pkt->size << std::endl; 114 | result = decode_packet(false); 115 | if (result < 0) { 116 | break; 117 | } 118 | } 119 | } 120 | } 121 | result = decode_packet(true); 122 | if (result < 0) { 123 | return result; 124 | } 125 | return 0; 126 | } 127 | 128 | void destroy_video_decoder() { 129 | av_parser_close(parser); 130 | avcodec_free_context(&codec_ctx); 131 | av_frame_free(&frame); 132 | av_packet_free(&pkt); 133 | } -------------------------------------------------------------------------------- /src/video_encoder_core.cpp: -------------------------------------------------------------------------------- 1 | // video_encoder_core.cpp 2 | #include "video_encoder_core.h" 3 | 4 | extern "C" { 5 | #include 6 | #include 7 | #include 8 | } 9 | 10 | #include 11 | #include 12 | 13 | #include "io_data.h" 14 | 15 | static AVCodec *codec = nullptr; 16 | static AVCodecContext *codec_ctx = nullptr; 17 | static AVFrame *frame = nullptr; 18 | static AVPacket *pkt = nullptr; 19 | 20 | int32_t init_video_encoder(const char *codec_name) { 21 | // 验证输入编码器名称非空 22 | if (strlen(codec_name) == 0) { 23 | std::cerr << "Error: empty codec name." << std::endl; 24 | return -1; 25 | } 26 | 27 | // 查找编码器 28 | codec = avcodec_find_encoder_by_name(codec_name); 29 | if (!codec) { 30 | std::cerr << "Error: could not find codec with codec name:" 31 | << std::string(codec_name) << std::endl; 32 | return -1; 33 | } 34 | 35 | // 创建编码器上下文结构 36 | codec_ctx = avcodec_alloc_context3(codec); 37 | if (!codec_ctx) { 38 | std::cerr << "Error: could not allocate video codec context." << std::endl; 39 | return -1; 40 | } 41 | 42 | // 配置编码参数 43 | codec_ctx->profile = FF_PROFILE_H264_HIGH; 44 | codec_ctx->bit_rate = 2000000; 45 | codec_ctx->width = 1280; 46 | codec_ctx->height = 720; 47 | codec_ctx->gop_size = 10; 48 | codec_ctx->time_base = (AVRational) {1, 25}; 49 | codec_ctx->framerate = (AVRational) {25, 1}; 50 | codec_ctx->max_b_frames = 3; 51 | codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; 52 | 53 | if (codec->id == AV_CODEC_ID_H264) { 54 | av_opt_set(codec_ctx->priv_data, "preset", "slow", 0); 55 | } 56 | 57 | // 使用指定的 codec 初始化编码器上下文结构 58 | int32_t result = avcodec_open2(codec_ctx, codec, nullptr); 59 | if (result < 0) { 60 | std::cerr << "Error: could not open codec" << std::endl; 61 | return -1; 62 | } 63 | 64 | pkt = av_packet_alloc(); 65 | if (!pkt) { 66 | std::cerr << "Error: could not allocate AVPacket." << std::endl; 67 | return -1; 68 | } 69 | 70 | frame = av_frame_alloc(); 71 | if (!frame) { 72 | std::cerr << "Error: could not allocate AVFrame." << std::endl; 73 | return -1; 74 | } 75 | frame->width = codec_ctx->width; 76 | frame->height = codec_ctx->height; 77 | frame->format = codec_ctx->pix_fmt; 78 | 79 | result = av_frame_get_buffer(frame, 0); 80 | if (result < 0) { 81 | std::cerr << "Error: could not get AVFrame buffer." << std::endl; 82 | return -1; 83 | } 84 | 85 | return 0; 86 | } 87 | 88 | void destroy_video_encoder() { 89 | // 释放编码器上下文结构 90 | avcodec_free_context(&codec_ctx); 91 | // 释放 Frame 和 Packet 结构 92 | av_frame_free(&frame); 93 | av_packet_free(&pkt); 94 | } 95 | 96 | static int32_t encode_frame(bool flushing) { 97 | int32_t result = 0; 98 | if (!flushing) { 99 | std::cout << "Send frame to encoder with pts: " << frame->pts << std::endl; 100 | } 101 | 102 | result = avcodec_send_frame(codec_ctx, flushing ? nullptr : frame); 103 | if (result < 0) { 104 | std::cerr << "Error: avcodec_send_frame failed." << std::endl; 105 | return result; 106 | } 107 | 108 | while (result >= 0) { 109 | result = avcodec_receive_packet(codec_ctx, pkt); 110 | if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) { 111 | return 1; 112 | } else if (result < 0) { 113 | std::cerr << "Error: avcodec_receive_packet failed." << std::endl; 114 | return result; 115 | } 116 | 117 | if (flushing) { 118 | std::cout << "Flushing:"; 119 | } 120 | std::cout << "Got encoded package with dts:" << pkt->dts 121 | << ", pts:" << pkt->pts << ", " << std::endl; 122 | write_pkt_to_file(pkt); 123 | } 124 | return 0; 125 | } 126 | 127 | int32_t encoding(int32_t frame_cnt) { 128 | int result = 0; 129 | for (size_t i = 0; i < frame_cnt; i++) { 130 | result = av_frame_make_writable(frame); 131 | if (result < 0) { 132 | std::cerr << "Error: could not av_frame_make_writable." << std::endl; 133 | return result; 134 | } 135 | 136 | result = read_yuv_to_frame(frame); 137 | if (result < 0) { 138 | std::cerr << "Error: read_yuv_to_frame failed." << std::endl; 139 | return result; 140 | } 141 | frame->pts = i; 142 | 143 | result = encode_frame(false); 144 | if (result < 0) { 145 | std::cerr << "Error: encode_frame failed." << std::endl; 146 | return result; 147 | } 148 | } 149 | result = encode_frame(true); 150 | if (result < 0) { 151 | std::cerr << "Error: flushing failed." << std::endl; 152 | return result; 153 | } 154 | 155 | return 0; 156 | } -------------------------------------------------------------------------------- /src/video_filter_core.cpp: -------------------------------------------------------------------------------- 1 | #include "video_filter_core.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "io_data.h" 9 | 10 | extern "C" { 11 | #include 12 | #include 13 | #include 14 | #include 15 | } 16 | 17 | #define STREAM_FRAME_RATE 25 18 | 19 | AVFilterContext *buffersink_ctx; 20 | AVFilterContext *buffersrc_ctx; 21 | AVFilterGraph *filter_graph; 22 | AVFrame *input_frame = nullptr, *output_frame = nullptr; 23 | 24 | static int32_t init_frames(int32_t width, int32_t height, 25 | enum AVPixelFormat pix_fmt) { 26 | int result = 0; 27 | input_frame = av_frame_alloc(); 28 | output_frame = av_frame_alloc(); 29 | if (!input_frame || !output_frame) { 30 | std::cerr << "Error: frame allocation failed." << std::endl; 31 | return -1; 32 | } 33 | 34 | input_frame->width = width; 35 | input_frame->height = height; 36 | input_frame->format = pix_fmt; 37 | 38 | result = av_frame_get_buffer(input_frame, 0); 39 | if (result < 0) { 40 | std::cerr << "Error: could not get AVFrame buffer." << std::endl; 41 | return -1; 42 | } 43 | 44 | result = av_frame_make_writable(input_frame); 45 | if (result < 0) { 46 | std::cerr << "Error: input frame is not writable." << std::endl; 47 | return -1; 48 | } 49 | return 0; 50 | } 51 | 52 | int32_t init_video_filter(int32_t width, int32_t height, 53 | const char *filter_descr) { 54 | int32_t result = 0; 55 | char args[512] = {0}; 56 | const AVFilter *buffersrc = avfilter_get_by_name("buffer"); 57 | const AVFilter *buffersink = avfilter_get_by_name("buffersink"); 58 | AVFilterInOut *outputs = avfilter_inout_alloc(); 59 | AVFilterInOut *inputs = avfilter_inout_alloc(); 60 | AVRational time_base = (AVRational) {1, STREAM_FRAME_RATE}; 61 | enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE}; 62 | 63 | do { 64 | filter_graph = avfilter_graph_alloc(); 65 | if (!outputs || !inputs || !filter_graph) { 66 | std::cerr << "Error: creating filter graph failed." << std::endl; 67 | result = AVERROR(ENOMEM); 68 | break; 69 | } 70 | 71 | snprintf(args, sizeof(args), 72 | "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", 73 | width, height, AV_PIX_FMT_YUV420P, 1, STREAM_FRAME_RATE, 1, 1); 74 | result = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, 75 | NULL, filter_graph); 76 | if (result < 0) { 77 | std::cerr << "Error: could not create source filter." << std::endl; 78 | break; 79 | } 80 | 81 | result = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", 82 | NULL, NULL, filter_graph); 83 | if (result < 0) { 84 | std::cerr << "Error: could not create sink filter." << std::endl; 85 | break; 86 | } 87 | 88 | result = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts, 89 | AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN); 90 | if (result < 0) { 91 | std::cerr << "Error: could not set output pixel format." << std::endl; 92 | break; 93 | } 94 | 95 | outputs->name = av_strdup("in"); 96 | outputs->filter_ctx = buffersrc_ctx; 97 | outputs->pad_idx = 0; 98 | outputs->next = NULL; 99 | 100 | inputs->name = av_strdup("out"); 101 | inputs->filter_ctx = buffersink_ctx; 102 | inputs->pad_idx = 0; 103 | inputs->next = NULL; 104 | 105 | if ((result = avfilter_graph_parse_ptr(filter_graph, filter_descr, &inputs, 106 | &outputs, NULL)) < 0) { 107 | std::cerr << "Error: avfilter_graph_parse_ptr failed" << std::endl; 108 | break; 109 | } 110 | 111 | if ((result = avfilter_graph_config(filter_graph, NULL)) < 0) { 112 | std::cerr << "Error: Graph config invalid." << std::endl; 113 | break; 114 | } 115 | 116 | result = init_frames(width, height, AV_PIX_FMT_YUV420P); 117 | if (result < 0) { 118 | std::cerr << "Error: init frames failed." << std::endl; 119 | break; 120 | } 121 | } while (0); 122 | 123 | avfilter_inout_free(&inputs); 124 | avfilter_inout_free(&outputs); 125 | return result; 126 | } 127 | 128 | static int32_t filter_frame() { 129 | int32_t result = 0; 130 | if ((result = av_buffersrc_add_frame_flags(buffersrc_ctx, input_frame, 131 | AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) { 132 | std::cerr << "Error: add frame to buffer src failed." << std::endl; 133 | return result; 134 | } 135 | 136 | while (1) { 137 | result = av_buffersink_get_frame(buffersink_ctx, output_frame); 138 | if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) { 139 | return 1; 140 | } else if (result < 0) { 141 | std::cerr << "Error: buffersink_get_frame failed." << std::endl; 142 | return result; 143 | } 144 | 145 | std::cout << "Frame filtered, width:" << output_frame->width 146 | << ", height : " << output_frame->height << std::endl; 147 | write_frame_to_yuv(output_frame); 148 | av_frame_unref(output_frame); 149 | } 150 | 151 | return result; 152 | } 153 | 154 | int32_t filtering_video(int32_t frame_cnt) { 155 | int32_t result = 0; 156 | for (size_t i = 0; i < frame_cnt; i++) { 157 | result = read_yuv_to_frame(input_frame); 158 | if (result < 0) { 159 | std::cerr << "Error: read_yuv_to_frame failed." << std::endl; 160 | return result; 161 | } 162 | 163 | result = filter_frame(); 164 | if (result < 0) { 165 | std::cerr << "Error: filter_frame failed." << std::endl; 166 | return result; 167 | } 168 | } 169 | return result; 170 | } 171 | 172 | static void free_frames() { 173 | av_frame_free(&input_frame); 174 | av_frame_free(&output_frame); 175 | } 176 | 177 | void destroy_video_filter() { 178 | free_frames(); 179 | avfilter_graph_free(&filter_graph); 180 | } 181 | -------------------------------------------------------------------------------- /src/video_swscale_core.cpp: -------------------------------------------------------------------------------- 1 | #include "video_swscale_core.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "io_data.h" 9 | 10 | extern "C" { 11 | #include 12 | #include 13 | #include 14 | } 15 | 16 | static AVFrame *input_frame = nullptr; 17 | static struct SwsContext *sws_ctx; 18 | static int32_t src_width = 0, src_height = 0, dst_width = 0, dst_height = 0; 19 | static enum AVPixelFormat src_pix_fmt = AV_PIX_FMT_NONE, 20 | dst_pix_fmt = AV_PIX_FMT_NONE; 21 | 22 | static int32_t init_frame(int32_t width, int32_t height, 23 | enum AVPixelFormat pix_fmt) { 24 | int result = 0; 25 | input_frame = av_frame_alloc(); 26 | if (!input_frame) { 27 | std::cerr << "Error: frame allocation failed." << std::endl; 28 | return -1; 29 | } 30 | 31 | input_frame->width = width; 32 | input_frame->height = height; 33 | input_frame->format = pix_fmt; 34 | 35 | result = av_frame_get_buffer(input_frame, 0); 36 | if (result < 0) { 37 | std::cerr << "Error: could not get AVFrame buffer." << std::endl; 38 | return -1; 39 | } 40 | 41 | result = av_frame_make_writable(input_frame); 42 | if (result < 0) { 43 | std::cerr << "Error: input frame is not writable." << std::endl; 44 | return -1; 45 | } 46 | return 0; 47 | } 48 | 49 | int32_t init_video_swscale(char *src_size, char *src_fmt, char *dst_size, 50 | char *dst_fmt) { 51 | int32_t result = 0; 52 | 53 | // 解析输入视频和输出视频的图像尺寸 54 | result = av_parse_video_size(&src_width, &src_height, src_size); 55 | if (result < 0) { 56 | std::cerr << "Error: Invalid input size. Must be in the form WxH or a " 57 | "valid size abbreviation.Input : " 58 | << std::string(src_size) << std::endl; 59 | return -1; 60 | } 61 | result = av_parse_video_size(&dst_width, &dst_height, dst_size); 62 | if (result < 0) { 63 | std::cerr << "Error: Invalid output size. Must be in the form WxH or a " 64 | "valid size abbreviation.Input : " 65 | << std::string(src_size) << std::endl; 66 | return -1; 67 | } 68 | 69 | // 选择输入视频和输出视频的图像格式 70 | if (!strcasecmp(src_fmt, "YUV420P")) { 71 | src_pix_fmt = AV_PIX_FMT_YUV410P; 72 | } else if (!strcasecmp(src_fmt, "RGB24")) { 73 | src_pix_fmt = AV_PIX_FMT_RGB24; 74 | } else { 75 | std::cerr << "Error: Unsupported input pixel format:" 76 | << std::string(src_fmt) << std::endl; 77 | return -1; 78 | } 79 | 80 | if (!strcasecmp(dst_fmt, "YUV420P")) { 81 | dst_pix_fmt = AV_PIX_FMT_YUV410P; 82 | } else if (!strcasecmp(dst_fmt, "RGB24")) { 83 | dst_pix_fmt = AV_PIX_FMT_RGB24; 84 | } else { 85 | std::cerr << "Error: Unsupported output pixel format:" 86 | << std::string(dst_fmt) << std::endl; 87 | return -1; 88 | } 89 | 90 | // 获取SwsContext结构 91 | sws_ctx = 92 | sws_getContext(src_width, src_height, src_pix_fmt, dst_width, dst_height, 93 | dst_pix_fmt, SWS_BILINEAR, NULL, NULL, NULL); 94 | if (!sws_ctx) { 95 | std::cerr << "Error: failed to get SwsContext." << std::endl; 96 | return -1; 97 | } 98 | 99 | // 初始化AVFrame结构 100 | result = init_frame(src_width, src_height, src_pix_fmt); 101 | if (result < 0) { 102 | std::cerr << "Error: failed to initialize input frame." << std::endl; 103 | return -1; 104 | } 105 | 106 | return result; 107 | } 108 | 109 | int32_t transforming(int32_t frame_cnt) { 110 | int32_t result = 0; 111 | uint8_t *dst_data[4]; 112 | int32_t dst_linesize[4] = {0}, dst_bufsize = 0; 113 | 114 | result = av_image_alloc(dst_data, dst_linesize, dst_width, dst_height, 115 | dst_pix_fmt, 1); 116 | if (result < 0) { 117 | std::cerr << "Error: failed to alloc output frame buffer." << std::endl; 118 | return -1; 119 | } 120 | dst_bufsize = result; 121 | 122 | for (int idx = 0; idx < frame_cnt; idx++) { 123 | result = read_yuv_to_frame(input_frame); 124 | if (result < 0) { 125 | std::cerr << "Error: read_yuv_to_frame failed." << std::endl; 126 | return result; 127 | } 128 | sws_scale(sws_ctx, input_frame->data, input_frame->linesize, 0, src_height, 129 | dst_data, dst_linesize); 130 | 131 | write_packed_data_to_file(dst_data[0], dst_bufsize); 132 | } 133 | 134 | av_freep(&dst_data[0]); 135 | return result; 136 | } 137 | 138 | void destroy_video_swscale() { 139 | av_frame_free(&input_frame); 140 | sws_freeContext(sws_ctx); 141 | } 142 | -------------------------------------------------------------------------------- /video/slamtv10.264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/video/slamtv10.264 -------------------------------------------------------------------------------- /video/test.264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lining1111/FFmpeg_Demo/5507ad36aa8adc8405d54f0dec6b733601b78743/video/test.264 --------------------------------------------------------------------------------