个人网站开发项目总结,做网站模板的网页名称是m开头,手机网站维护费,游戏类网站欣赏文章目录 前言一、FFmpeg 解码流程二、FFmpeg 转码流程三、编解码 API 详解1、解码 API 使用详解2、编码 API 使用详解 四、编码案例实战1、示例源码2、运行结果 五、解码案例实战1、示例源码2、运行结果 前言
AVFormatContext 是一个贯穿始终的数据结构#xff0c;很多函数都… 文章目录 前言一、FFmpeg 解码流程二、FFmpeg 转码流程三、编解码 API 详解1、解码 API 使用详解2、编码 API 使用详解 四、编码案例实战1、示例源码2、运行结果 五、解码案例实战1、示例源码2、运行结果 前言
AVFormatContext 是一个贯穿始终的数据结构很多函数都用到它作为参数是输入输出相关信息的一个容器本文讲解 AVFormatContext 的编解码层主要包括三大数据结构AVStreamAVCodecContexAVCodec。 一、FFmpeg 解码流程 得到输入文件 - 解封格式 - 得到编码的数据包 - 解码数据包 - 得到解码后的数据帧 - 处理数据帧 - 编码 - 得到编码后的数据包 - 封装格式 - 输出文件
涉及到下面的 API 函数
注册所有容器格式和 CODECav_register_all()打开文件av_open_input_file()从文件中提取流信息av_find_stream_info()穷举所有的流查找其中种类为 CODEC_TYPE_VIDEO查找对应的解码器avcodec_find_decoder()打开编解码器avcodec_open()为解码帧分配内存avcodec_alloc_frame()不停地从码流中提取出帧数据av_read_frame()判断帧的类型对于视频帧调用avcodec_decode_video()解码完后释放解码器avcodec_close()关闭输入文件av_close_input_file()
程序流程图如下图所示
二、FFmpeg 转码流程 大流程可以划分为输入、输出、转码、播放四大块其中转码涉及比较多的处理环节从图中可以看出转码功能在整个功能图中占比很大。转码的核心功能在解码和编码两个部分但在一个可用的示例程序中编码解码与输入输出是难以分割的。解复用器为解码器提供输入解码器会输出原始帧对原始帧可进行各种复杂的滤镜处理滤镜处理后的帧经编码器生成编码帧多路流的编码帧经复用器输出到输出文件。
三、编解码 API 详解
解码使用 avcodec_send_packet() 和 avcodec_receive_frame() 两个函数。编码使用 avcodec_send_frame() 和 avcodec_receive_packet() 两个函数。
1、解码 API 使用详解
关于 avcodec_send_packet() 与 avcodec_receive_frame() 的使用说明
①、按 dts 递增的顺序向解码器送入编码帧 packet解码器按 pts 递增的顺序输出原始帧 frame实际上解码器不关注输入 packet 的 dts错值都没关系它只管依次处理收到的 packet按需缓冲和解码②、avcodec_receive_frame() 输出 frame 时会根据各种因素设置好 frame-best_effort_timestamp文档明确说明实测 frame-pts 也会被设置通常直接拷贝自对应的 packet.pts文档未明确说明用户应确保 avcodec_send_packet() 发送的 packet 具有正确的 pts编码帧 packet 与原始帧 frame 间的对应关系通过 pts 确定③、avcodec_receive_frame() 输 出 frame 时 frame-pkt_dts 拷贝自当前 avcodec_send_packet() 发送的 packet 中的 dts 如果当前 packet 为 NULLflush packet解码器进入 flush 模式当前及剩余的 frame-pkt_dts 值总为 AV_NOPTS_VALUE。因为解码器中有缓存帧当前输出的 frame 并不是由当前输入的 packet 解码得到的所以这个 frame-pkt_dts 没什么实际意义可以不必关注④、avcodec_send_packet() 发送第一个 NULL 会返回成功后续的 NULL 会返回 AVERROR_EOF⑤、avcodec_send_packet() 多次发送 NULL 并不会导致解码器中缓存的帧丢失使用 avcodec_flush_buffers() 可以立即丢掉解码器中缓存帧。因此播放完毕时应 avcodec_send_packet(NULL) 来取完缓存的帧而 SEEK 操作或切换流时应调用 avcodec_flush_buffers() 来直接丢弃缓存帧⑥、解码器通常的冲洗方法调用一次 avcodec_send_packet(NULL)(返回成功)然后不停调用 avcodec_receive_frame() 直到其返回 AVERROR_EOF取出所有缓存帧avcodec_receive_frame() 返回 AVERROR_EOF 这一次是没有有效数据的仅仅获取到一个结束标志
2、编码 API 使用详解
关于 avcodec_send_frame() 与 avcodec_receive_packet() 的使用说明
①、按 pts 递增的顺序向编码器送入原始帧 frame 编码器按 dts 递增的顺序输出编码帧 packet实际上编码器关注输入 frame 的 pts 不关注其 dts它只管依次处理收到的 frame按需缓冲和编码②、avcodec_receive_packet() 输出 packet 时会设置 packet.dts从 0 开始每次输出的 packet 的 dts 加 1这是视频层的 dts用户写输出前应将其转换为容器层的 dts③、avcodec_receive_packet() 输出 packet 时packet.pts 拷贝自对应的 frame.pts这是视频层的 pts用户写输出前应将其转换为容器层的 pts④、avcodec_send_frame() 发送 NULL frame 时 编码器进入 flush 模式⑤、avcodec_send_frame() 发送第一个 NULL 会返回成功 后续的 NULL 会返回 AVERROR_EOF⑥、avcodec_send_frame() 多次发送 NULL 并不会导致编码器中缓存的帧丢失使用 avcodec_flush_buffers() 可以立即丢掉编码器中缓存帧。因此编码完毕时应使用 avcodec_send_frame(NULL) 来取完缓存的帧而 SEEK 操作或切换流时应调用 avcodec_flush_buffers() 来直接丢弃缓存帧⑦、编码器通常的冲洗方法调用一次 avcodec_send_frame(NULL)(返回成功)然后不停调用avcodec_receive_packet() 直到其返回 AVERROR_EOF取出所有缓存帧avcodec_receive_packet() 返回AVERROR_EOF 这一次是没有有效数据的仅仅获取到一个结束标志⑧、对音频来说如果 AV_CODEC_CAP_VARIABLE_FRAME_SIZE在 AVCodecContext.codec.capabilities 变量中只读标志有效表示编码器支持可变尺寸音频帧送入编码器的音频帧可以包含任意数量的采样点。如果此标志无效则每一个音频帧的采样点数目frame-nb_samples必须等于编码器设定的音频帧尺寸avctx-frame_size最后一帧除外最后一帧音频帧采样点数可以小于 avctx-frame_size
四、编码案例实战
下面代码使用实现了一个简单的视频编码器将指定的图像序列编码为 MPEG 视频文件
1、示例源码
#include stdio.h
#include stdlib.h
#include string.h#include libavcodec/avcodec.h#include libavutil/opt.h
#include libavutil/imgutils.hstatic void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,FILE *outfile)
{int ret;/* send the frame to the encoder */if (frame)printf(Send frame %3PRId64\n, frame-pts);ret avcodec_send_frame(enc_ctx, frame);if (ret 0) {fprintf(stderr, Error sending a frame for encoding\n);exit(1);}while (ret 0) {ret avcodec_receive_packet(enc_ctx, pkt);if (ret AVERROR(EAGAIN) || ret AVERROR_EOF)return;else if (ret 0) {fprintf(stderr, Error during encoding\n);exit(1);}printf(Write packet %3PRId64 (size%5d)\n, pkt-pts, pkt-size);fwrite(pkt-data, 1, pkt-size, outfile);av_packet_unref(pkt);}
}int main(int argc, char **argv)
{const char *filename, *codec_name;const AVCodec *codec;AVCodecContext *c NULL;int i, ret, x, y;FILE *f;AVFrame *frame;AVPacket *pkt;uint8_t endcode[] { 0, 0, 1, 0xb7 };filename ./debug/test666.mp4;codec_name mpeg1video;/* find the mpeg1video encoder */codec avcodec_find_encoder_by_name(codec_name);if (!codec) {fprintf(stderr, Codec %s not found\n, codec_name);exit(1);}c avcodec_alloc_context3(codec);if (!c) {fprintf(stderr, Could not allocate video codec context\n);exit(1);}pkt av_packet_alloc();if (!pkt)exit(1);/* put sample parameters */c-bit_rate 400000;/* resolution must be a multiple of two */c-width 352;c-height 288;/* frames per second */c-time_base (AVRational){1, 25};c-framerate (AVRational){25, 1};/* emit one intra frame every ten frames* check frame pict_type before passing frame* to encoder, if frame-pict_type is AV_PICTURE_TYPE_I* then gop_size is ignored and the output of encoder* will always be I frame irrespective to gop_size*/c-gop_size 10;c-max_b_frames 1;c-pix_fmt AV_PIX_FMT_YUV420P;if (codec-id AV_CODEC_ID_H264)av_opt_set(c-priv_data, preset, slow, 0);/* open it */ret avcodec_open2(c, codec, NULL);if (ret 0) {fprintf(stderr, Could not open codec: %s\n, av_err2str(ret));exit(1);}f fopen(filename, wb);if (!f) {fprintf(stderr, Could not open %s\n, filename);exit(1);}frame av_frame_alloc();if (!frame) {fprintf(stderr, Could not allocate video frame\n);exit(1);}frame-format c-pix_fmt;frame-width c-width;frame-height c-height;ret av_frame_get_buffer(frame, 0);if (ret 0) {fprintf(stderr, Could not allocate the video frame data\n);exit(1);}/* encode 1 second of video */for (i 0; i 25; i) {fflush(stdout);/* make sure the frame data is writable */ret av_frame_make_writable(frame);if (ret 0)exit(1);/* prepare a dummy image *//* Y */for (y 0; y c-height; y) {for (x 0; x c-width; x) {frame-data[0][y * frame-linesize[0] x] x y i * 3;}}/* Cb and Cr */for (y 0; y c-height/2; y) {for (x 0; x c-width/2; x) {frame-data[1][y * frame-linesize[1] x] 128 y i * 2;frame-data[2][y * frame-linesize[2] x] 64 x i * 5;}}frame-pts i;/* encode the image */encode(c, frame, pkt, f);}/* flush the encoder */encode(c, NULL, pkt, f);/* add sequence end code to have a real MPEG file */if (codec-id AV_CODEC_ID_MPEG1VIDEO || codec-id AV_CODEC_ID_MPEG2VIDEO)fwrite(endcode, 1, sizeof(endcode), f);fclose(f);avcodec_free_context(c);av_frame_free(frame);av_packet_free(pkt);return 0;
}2、运行结果
Send frame 0
Send frame 1
Write packet 0 (size 6731)
Send frame 2
Write packet 2 (size 3727)
Send frame 3
Write packet 1 (size 1680)
Send frame 4
Write packet 4 (size 2744)
Send frame 5
Write packet 3 (size 1678)
Send frame 6
Write packet 6 (size 2963)
Send frame 7
Write packet 5 (size 1819)
Send frame 8
Write packet 8 (size 3194)
Send frame 9
Write packet 7 (size 1977)
Send frame 10
Write packet 10 (size12306)
Send frame 11
Write packet 9 (size 2231)
Send frame 12
Write packet 12 (size 3762)
Send frame 13
Write packet 11 (size 2039)
[mpeg1video 02562100] warning, clipping 1 dct coefficients to -255..255
....
[mpeg1video 02562100] warning, clipping 1 dct coefficients to -255..255
[mpeg1video 02562100] waSend frame 14
Write packet 14 (size 3278)
Send frame 15
Write packet 13 (size 1939)
Send frame 16
Write packet 16 (size 3150)
Send frame 17
Write packet 15 (size 1929)
Send frame 18
Write packet 18 (size 3422)
Send frame 19
Write packet 17 (size 2116)
Send frame 20
Write packet 20 (size12236)
Send frame 21
Write packet 19 (size 2055)
Send frame 22
Write packet 22 (size 4054)
Send frame 23
Write packet 21 (size 2048)
Send frame 24
Write packet 24 (size 3191)
Write packet 23 (size 1955)
rning, clipping 1 dct coefficients to -255..255
[mpeg1video 02562100] warning, clipping 1 dct coefficients to -255..255
....
[mpeg1video 02562100] warning, clipping 1 dct coefficients to -255..255在 debug 目录下生成一个 test666.mp4使用 MediaInfo 查看相关信息如下 可以看到只有一路视频流。使用 VLC 播放可以看到 1s 的视频如下
五、解码案例实战
下面代码使用实现了一个视频解码将指定的视频 test.flv 解码为原始视频数据 yuv 和原始音频数据 pcm
1、示例源码 /*** file* Demuxing and decoding example.** Show how to use the libavformat and libavcodec API to demux and* decode audio and video data.* example demuxing_decoding.c*/#include libavutil/imgutils.h
#include libavutil/samplefmt.h
#include libavutil/timestamp.h
#include libavformat/avformat.hstatic AVFormatContext *fmt_ctx NULL;
static AVCodecContext *video_dec_ctx NULL, *audio_dec_ctx;
static int width, height;
static enum AVPixelFormat pix_fmt;
static AVStream *video_stream NULL, *audio_stream NULL;
static const char *src_filename NULL;
static const char *video_dst_filename NULL;
static const char *audio_dst_filename NULL;
static FILE *video_dst_file NULL;
static FILE *audio_dst_file NULL;static uint8_t *video_dst_data[4] {NULL};
static int video_dst_linesize[4];
static int video_dst_bufsize;static int video_stream_idx -1, audio_stream_idx -1;
static AVFrame *frame NULL;
static AVPacket pkt;
static int video_frame_count 0;
static int audio_frame_count 0;static int output_video_frame(AVFrame *frame)
{if (frame-width ! width || frame-height ! height ||frame-format ! pix_fmt) {/* To handle this change, one could call av_image_alloc again and* decode the following frames into another rawvideo file. */fprintf(stderr, Error: Width, height and pixel format have to be constant in a rawvideo file, but the width, height or pixel format of the input video changed:\nold: width %d, height %d, format %s\nnew: width %d, height %d, format %s\n,width, height, av_get_pix_fmt_name(pix_fmt),frame-width, frame-height,av_get_pix_fmt_name(frame-format));return -1;}printf(video_frame n:%d coded_n:%d\n,video_frame_count, frame-coded_picture_number);/* copy decoded frame to destination buffer:* this is required since rawvideo expects non aligned data */av_image_copy(video_dst_data, video_dst_linesize,(const uint8_t **)(frame-data), frame-linesize,pix_fmt, width, height);/* write to rawvideo file */fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file);return 0;
}static int output_audio_frame(AVFrame *frame)
{size_t unpadded_linesize frame-nb_samples * av_get_bytes_per_sample(frame-format);printf(audio_frame n:%d nb_samples:%d pts:%s\n,audio_frame_count, frame-nb_samples,av_ts2timestr(frame-pts, audio_dec_ctx-time_base));/* Write the raw audio data samples of the first plane. This works* fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However,* most audio decoders output planar audio, which uses a separate* plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P).* In other words, this code will write only the first audio channel* in these cases.* You should use libswresample or libavfilter to convert the frame* to packed data. */fwrite(frame-extended_data[0], 1, unpadded_linesize, audio_dst_file);return 0;
}static int decode_packet(AVCodecContext *dec, const AVPacket *pkt)
{int ret 0;// submit the packet to the decoderret avcodec_send_packet(dec, pkt);if (ret 0) {fprintf(stderr, Error submitting a packet for decoding (%s)\n, av_err2str(ret));return ret;}// get all the available frames from the decoderwhile (ret 0) {ret avcodec_receive_frame(dec, frame);if (ret 0) {// those two return values are special and mean there is no output// frame available, but there were no errors during decodingif (ret AVERROR_EOF || ret AVERROR(EAGAIN))return 0;fprintf(stderr, Error during decoding (%s)\n, av_err2str(ret));return ret;}// write the frame data to output fileif (dec-codec-type AVMEDIA_TYPE_VIDEO)ret output_video_frame(frame);elseret output_audio_frame(frame);av_frame_unref(frame);if (ret 0)return ret;}return 0;
}static int open_codec_context(int *stream_idx,AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type)
{int ret, stream_index;AVStream *st;AVCodec *dec NULL;AVDictionary *opts NULL;ret av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);if (ret 0) {fprintf(stderr, Could not find %s stream in input file %s\n,av_get_media_type_string(type), src_filename);return ret;} else {stream_index ret;st fmt_ctx-streams[stream_index];/* find decoder for the stream */dec avcodec_find_decoder(st-codecpar-codec_id);if (!dec) {fprintf(stderr, Failed to find %s codec\n,av_get_media_type_string(type));return AVERROR(EINVAL);}/* Allocate a codec context for the decoder */*dec_ctx avcodec_alloc_context3(dec);if (!*dec_ctx) {fprintf(stderr, Failed to allocate the %s codec context\n,av_get_media_type_string(type));return AVERROR(ENOMEM);}/* Copy codec parameters from input stream to output codec context */if ((ret avcodec_parameters_to_context(*dec_ctx, st-codecpar)) 0) {fprintf(stderr, Failed to copy %s codec parameters to decoder context\n,av_get_media_type_string(type));return ret;}/* Init the decoders */if ((ret avcodec_open2(*dec_ctx, dec, opts)) 0) {fprintf(stderr, Failed to open %s codec\n,av_get_media_type_string(type));return ret;}*stream_idx stream_index;}return 0;
}static int get_format_from_sample_fmt(const char **fmt,enum AVSampleFormat sample_fmt)
{int i;struct sample_fmt_entry {enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le;} sample_fmt_entries[] {{ AV_SAMPLE_FMT_U8, u8, u8 },{ AV_SAMPLE_FMT_S16, s16be, s16le },{ AV_SAMPLE_FMT_S32, s32be, s32le },{ AV_SAMPLE_FMT_FLT, f32be, f32le },{ AV_SAMPLE_FMT_DBL, f64be, f64le },};*fmt NULL;for (i 0; i FF_ARRAY_ELEMS(sample_fmt_entries); i) {struct sample_fmt_entry *entry sample_fmt_entries[i];if (sample_fmt entry-sample_fmt) {*fmt AV_NE(entry-fmt_be, entry-fmt_le);return 0;}}fprintf(stderr,sample format %s is not supported as output format\n,av_get_sample_fmt_name(sample_fmt));return -1;
}int main(int argc, char **argv)
{int ret 0;src_filename ./debug/test.flv;video_dst_filename ./debug/test.yuv;audio_dst_filename ./debug/test.pcm;/* open input file, and allocate format context */if (avformat_open_input(fmt_ctx, src_filename, NULL, NULL) 0) {fprintf(stderr, Could not open source file %s\n, src_filename);exit(1);}/* retrieve stream information */if (avformat_find_stream_info(fmt_ctx, NULL) 0) {fprintf(stderr, Could not find stream information\n);exit(1);}if (open_codec_context(video_stream_idx, video_dec_ctx, fmt_ctx, AVMEDIA_TYPE_VIDEO) 0) {video_stream fmt_ctx-streams[video_stream_idx];video_dst_file fopen(video_dst_filename, wb);if (!video_dst_file) {fprintf(stderr, Could not open destination file %s\n, video_dst_filename);ret 1;goto end;}/* allocate image where the decoded image will be put */width video_dec_ctx-width;height video_dec_ctx-height;pix_fmt video_dec_ctx-pix_fmt;ret av_image_alloc(video_dst_data, video_dst_linesize,width, height, pix_fmt, 1);if (ret 0) {fprintf(stderr, Could not allocate raw video buffer\n);goto end;}video_dst_bufsize ret;}if (open_codec_context(audio_stream_idx, audio_dec_ctx, fmt_ctx, AVMEDIA_TYPE_AUDIO) 0) {audio_stream fmt_ctx-streams[audio_stream_idx];audio_dst_file fopen(audio_dst_filename, wb);if (!audio_dst_file) {fprintf(stderr, Could not open destination file %s\n, audio_dst_filename);ret 1;goto end;}}/* dump input information to stderr */av_dump_format(fmt_ctx, 0, src_filename, 0);if (!audio_stream !video_stream) {fprintf(stderr, Could not find audio or video stream in the input, aborting\n);ret 1;goto end;}frame av_frame_alloc();if (!frame) {fprintf(stderr, Could not allocate frame\n);ret AVERROR(ENOMEM);goto end;}/* initialize packet, set data to NULL, let the demuxer fill it */av_init_packet(pkt);pkt.data NULL;pkt.size 0;if (video_stream)printf(Demuxing video from file %s into %s\n, src_filename, video_dst_filename);if (audio_stream)printf(Demuxing audio from file %s into %s\n, src_filename, audio_dst_filename);/* read frames from the file */while (av_read_frame(fmt_ctx, pkt) 0) {// check if the packet belongs to a stream we are interested in, otherwise// skip itif (pkt.stream_index video_stream_idx)ret decode_packet(video_dec_ctx, pkt);else if (pkt.stream_index audio_stream_idx)ret decode_packet(audio_dec_ctx, pkt);av_packet_unref(pkt);if (ret 0)break;}/* flush the decoders */if (video_dec_ctx)decode_packet(video_dec_ctx, NULL);if (audio_dec_ctx)decode_packet(audio_dec_ctx, NULL);printf(Demuxing succeeded.\n);if (video_stream) {printf(Play the output video file with the command:\nffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n,av_get_pix_fmt_name(pix_fmt), width, height,video_dst_filename);}if (audio_stream) {enum AVSampleFormat sfmt audio_dec_ctx-sample_fmt;int n_channels audio_dec_ctx-channels;const char *fmt;if (av_sample_fmt_is_planar(sfmt)) {const char *packed av_get_sample_fmt_name(sfmt);printf(Warning: the sample format the decoder produced is planar (%s). This example will output the first channel only.\n,packed ? packed : ?);sfmt av_get_packed_sample_fmt(sfmt);n_channels 1;}if ((ret get_format_from_sample_fmt(fmt, sfmt)) 0)goto end;printf(Play the output audio file with the command:\nffplay -f %s -ac %d -ar %d %s\n,fmt, n_channels, audio_dec_ctx-sample_rate,audio_dst_filename);}end:avcodec_free_context(video_dec_ctx);avcodec_free_context(audio_dec_ctx);avformat_close_input(fmt_ctx);if (video_dst_file)fclose(video_dst_file);if (audio_dst_file)fclose(audio_dst_file);av_frame_free(frame);av_free(video_dst_data[0]);return ret 0;
}2、运行结果
Demuxing video from file ./debug/test.flv into ./debug/test.yuv
Demuxing audio from file ./debug/test.flv into ./debug/test.pcm
video_frame n:0 coded_n:0
audio_frame n:0 nb_samples:1024 pts:0
audio_frame n:1 nb_samples:1024 pts:0.0004375
video_frame n:1 coded_n:1
audio_frame n:2 nb_samples:1024 pts:0.000895833
audio_frame n:3 nb_samples:1024 pts:0.00133333
video_frame n:2 coded_n:2
audio_frame n:4 nb_samples:1024 pts:0.00177083
audio_frame n:5 nb_samples:1024 pts:0.00222917
....
video_frame n:2931 coded_n:2931
audio_frame n:5496 nb_samples:1024 pts:2.44267
audio_frame n:5497 nb_samples:1024 pts:2.4431
audio_frame n:5498 nb_samples:1024 pts:2.44356
Demuxing succeeded.
Play the output video file with the command:
ffplay -f rawvideo -pix_fmt yuv420p -video_size 1280x720 ./debug/test.yuv
Warning: the sample format the decoder produced is planar (fltp). This example will output the first channel only.
Play the output audio file with the command:
ffplay -f f32le -ac 1 -ar 48000 ./debug/test.pcm
Input #0, flv, from ./debug/test.flv:Metadata:encoder : Lavf58.45.100Duration: 00:01:57.31, start: 0.000000, bitrate: 1442 kb/sStream #0:0: Video: h264 (Main), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], 1048 kb/s, 25 fps, 25 tbr, 1k tbn, 50 tbcStream #0:1: Audio: aac (LC), 48000 Hz, 5.1, fltp, 383 kb/s在 debug 目录下生成了原始视频数据 test.yuv 以及原始音频数据 test.pcm 在 debug 目录下打开终端使用 ffplay 播放 test.yuv
ffplay -f rawvideo -video_size 1280x720 test.yuv此时可以看到原视频中的视频无音频。
在 debug 目录下打开终端使用 ffplay 播放 test.pcm
ffplay -f f32le -ac 1 -ar 48000 test.pcm此时可以听到原视频中的音频无视频。 我的qq2442391036欢迎交流