当前位置: 首页 > news >正文

用html怎么做网站尾部wordpress有什么缺点

用html怎么做网站尾部,wordpress有什么缺点,万网备案域名购买,河南瑞达建设工程有限公司网站这一节#xff0c;接 音视频开发12 FFmpeg 解复用详情分析#xff0c;前面我们已经对一个 MP4文件#xff0c;或者 FLV文件#xff0c;或者TS文件进行了 解复用#xff0c;解出来的 视频是H264,音频是AAC#xff0c;那么接下来就要对H264和AAC进行处理#xff0c;这一节… 这一节接 音视频开发12 FFmpeg 解复用详情分析前面我们已经对一个 MP4文件或者 FLV文件或者TS文件进行了 解复用解出来的 视频是H264,音频是AAC那么接下来就要对H264和AAC进行处理这一节 主要是对 AAC进行处理。 ⾳频解码过程 FFmpeg流程解码过程 关键函数说明 1.找到想要的编解码器 const AVCodec *avcodec_find_decoder(enum AVCodecID id); 根据AVCodecID 查找对应的AVCodec 根据AVCodecID 查找对应的AVCodec /*** Find a registered decoder with a matching codec ID.** param id AVCodecID of the requested decoder* return A decoder if one was found, NULL otherwise.*/ const AVCodec *avcodec_find_decoder(enum AVCodecID id);这个 AVCodecID 代表的是 解码器 或者 编码器 的 IDenum AVCodecID {AV_CODEC_ID_NONE,......//video codecsAV_CODEC_ID_H264,......//audio codecsAV_CODEC_ID_MP3,AV_CODEC_ID_AAC,......//pcm codecsAV_CODEC_ID_PCM_U16LE//subtitle codecsAV_CODEC_ID_DVD_SUBTITLE} 但是这里有一个问题就是我们一般在解析一个文件的时候并不知道这个文件的音频和视频用的什么编码也就不知道用什么解码器解码比较好合理的写法有两种如下 第一种在前面解封装的时候通过 avformat_find_stream_info 方法我们得到过文件的详细信息然后通过 avformatcontext 得到 每一个AVStream通过AVStream就可以得到codecid,然后就可以得到AVCodec。 但是这里无法分清楚那个是音频哪个是视频还需要进一步的判断 for (i 0; i ifmt_ctx-nb_streams; i) {AVStream *stream avformatcontext-streams[i];const AVCodec *dec avcodec_find_decoder(stream-codecpar-codec_id);。。。。。。} 另一种方式使用 av_find_best_stream 函数 获得指定的 avformatcontext中的最佳的stream。这时候通过传递进去一个 AVCodec方法完成后就能得到对应的AVCodec 注意你要得到的 解码器 avcodec是通过指针的形式传递进去的。 int av_find_best_stream(AVFormatContext *avformatcontext,enum AVMediaType type,int wanted_stream_nb,int related_stream,const struct AVCodec **decoder_ret,int flags);参数说明 icAVFormatContext指针表示输入的媒体文件上下文。 type要查找的媒体流类型可以是音频流、视频流或字幕流等。 wanted_stream_nb期望的媒体流索引号可以是特定的索引号也可以是AV_NOPTS_VALUE-1表示任意流。 related_stream前一个相关流的索引号如果没有前一个相关流则传入-1。 decoder_ret返回解码器指针。 flags查找最佳流的标志位默认为0。 返回值 找到的最佳匹配媒体流的索引号如果找不到则返回AVERROR_STREAM_NOT_FOUND。* return the non-negative stream number in case of success,* AVERROR_STREAM_NOT_FOUND if no stream with the requested type* could be found,* AVERROR_DECODER_NOT_FOUND if streams were found but no decoder** note If av_find_best_stream returns successfully and decoder_ret is not* NULL, then *decoder_ret is guaranteed to be set to a valid AVCodec.•avcodec_find_decoder_by_name():根据解码器名字 找到解码器这里有一个问题这个name从哪里得到呢 在windows cmd 下输入 ffmpeg -h就可以看到 Print help / information / capabilities: -L show license -h topic show help -version show version -muxers show available muxers -demuxers show available demuxers -devices show available devices -decoders show available decoders -encoders show available encoders -filters show available filters -pix_fmts show available pixel formats -layouts show standard channel layouts -sample_fmts show available audio sample formats 我们是要找解码器的因此 ffmpeg  -decoders 就可以将所有的解码器列出来为了方便查找还可以将存储到 一个txt 中 ffmpeg  -decoders a.txt 在a.txt中看当前ffmpeg 支持的 decoder 的name有哪些对应的如下的012v4xm就是video的解码器名字也可以当前查找关键字例如aach264 就更快一些。 Decoders:V..... VideoA..... AudioS..... Subtitle.F.... Frame-level multithreading..S... Slice-level multithreading...X.. Codec is experimental....B. Supports draw_horiz_band.....D Supports direct rendering method 1------V....D 012v Uncompressed 4:2:2 10-bitV....D 4xm 4X MovieV....D 8bps QuickTime 8BPS video ...................A....D aac AAC (Advanced Audio Coding)A....D aac_fixed AAC (Advanced Audio Coding) (codec aac)A....D libfdk_aac Fraunhofer FDK AAC (codec aac)A....D aac_latm AAC LATM (Advanced Audio Coding LATM syntax) .................V....D h261 H.261V...BD h263 H.263 / H.263-1996, H.263 / H.263-1998 / H.263 version 2V...BD h263i Intel H.263V...BD h263p H.263 / H.263-1996, H.263 / H.263-1998 / H.263 version 2VFS..D h264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10VFS..D hap Vidvox HapVF...D hdr HDR (Radiance RGBE format) image /*** Find a registered decoder with the specified name.** param name name of the requested decoder* return A decoder if one was found, NULL otherwise.*/ const AVCodec *avcodec_find_decoder_by_name(const char *name); 到这里我们就有了解码器了AVCodec有了解码器还不行还需要有解码器上下文这里谈一下为什么有了解码器 还需要有 解码器上下文。 假设有一个视频文件里面有3路视频3路音频有两路视频都是H264的如果数据都保存到解码器里面多路解码的时候数据会有冲突因此要多设计一个AVCodecContext. 也就是说在ffmpeg 中AVCodec 中一般存储的是方法AVCodecContext 中则存储了该AVCodec中的具体数据。实际上 ffmpeg 中一直就延续这种风格xxxcontext中存储的都是对应的xxx的数据而 xxx中则是对应的方法之类的。 我们具体的来看struct AVCodec 我们观察AVCodec 看到AVCodec 中的内容都是  该 AVCodec支持的supported_framerates 数组。 该 AVCodec支持的 enum AVPixelFormat *pix_fmt 数组。 该 AVCodec支持的 supported_samplerates 数组。 该 AVCodec支持的 AVSampleFormat 数组。 typedef struct AVCodec {/*** Name of the codec implementation.* The name is globally unique among encoders and among decoders (but an* encoder and a decoder can share the same name).* This is the primary way to find a codec from the user perspective.*/const char *name;/*** Descriptive name for the codec, meant to be more human readable than name.* You should use the NULL_IF_CONFIG_SMALL() macro to define it.*/const char *long_name;enum AVMediaType type;enum AVCodecID id;/*** Codec capabilities.* see AV_CODEC_CAP_**/int capabilities;uint8_t max_lowres; /// maximum value for lowres supported by the decoderconst AVRational *supported_framerates; /// array of supported framerates, or NULL if any, array is terminated by {0,0}const enum AVPixelFormat *pix_fmts; /// array of supported pixel formats, or NULL if unknown, array is terminated by -1const int *supported_samplerates; /// array of supported audio samplerates, or NULL if unknown, array is terminated by 0const enum AVSampleFormat *sample_fmts; /// array of supported sample formats, or NULL if unknown, array is terminated by -1const AVClass *priv_class; /// AVClass for the private contextconst AVProfile *profiles; /// array of recognized profiles, or NULL if unknown, array is terminated by {AV_PROFILE_UNKNOWN}/*** Group name of the codec implementation.* This is a short symbolic name of the wrapper backing this codec. A* wrapper uses some kind of external implementation for the codec, such* as an external library, or a codec implementation provided by the OS or* the hardware.* If this field is NULL, this is a builtin, libavcodec native codec.* If non-NULL, this will be the suffix in AVCodec.name in most cases* (usually AVCodec.name will be of the form codec_name_wrapper_name).*/const char *wrapper_name;/*** Array of supported channel layouts, terminated with a zeroed layout.*/const AVChannelLayout *ch_layouts; } AVCodec; 再来看一下 AVCodecContext 的内容。里面存储了当前的avcodec的具体的数据我们的这个 AVCodecContext  内容太多了。 这里如果要看直接看源码比较好 2. 给解码器 分配 解码器上下文并初始化一些default value注意这时候 解码器上下文还是没有值 我们在这里debug 一下看这时候 AVCodecContext 里面的内容是啥 AVCodecContext *avcodec_alloc_context3(const AVCodec *codec); /*** Allocate an AVCodecContext and set its fields to default values. The* resulting struct should be freed with avcodec_free_context().** param codec if non-NULL, allocate private data and initialize defaults* for the given codec. It is illegal to then call avcodec_open2()* with a different codec.* If NULL, then the codec-specific defaults wont be initialized,* which may result in suboptimal default settings (this is* important mainly for encoders, e.g. libx264).** return An AVCodecContext filled with default values or NULL on failure.*/ AVCodecContext *avcodec_alloc_context3(const AVCodec *codec); 3. 打开 解码器上下文  avcodec_open2 int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); /*** Initialize the AVCodecContext to use the given AVCodec. Prior to using this* function the context has to be allocated with avcodec_alloc_context3().** The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(),* avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for* retrieving a codec.** Depending on the codec, you might need to set options in the codec context* also for decoding (e.g. width, height, or the pixel or audio sample format in* the case the information is not available in the bitstream, as when decoding* raw audio or video).** Options in the codec context can be set either by setting them in the options* AVDictionary, or by setting the values in the context itself, directly or by* using the av_opt_set() API before calling this function.** Example:* code* av_dict_set(opts, b, 2.5M, 0);* codec avcodec_find_decoder(AV_CODEC_ID_H264);* if (!codec)* exit(1);** context avcodec_alloc_context3(codec);** if (avcodec_open2(context, codec, opts) 0)* exit(1);* endcode** In the case AVCodecParameters are available (e.g. when demuxing a stream* using libavformat, and accessing the AVStream contained in the demuxer), the* codec parameters can be copied to the codec context using* avcodec_parameters_to_context(), as in the following example:** code* AVStream *stream ...;* context avcodec_alloc_context3(codec);* if (avcodec_parameters_to_context(context, stream-codecpar) 0)* exit(1);* if (avcodec_open2(context, codec, NULL) 0)* exit(1);* endcode** note Always call this function before using decoding routines (such as* ref avcodec_receive_frame()).** param avctx The context to initialize.* param codec The codec to open this context for. If a non-NULL codec has been* previously passed to avcodec_alloc_context3() or* for this context, then this parameter MUST be either NULL or* equal to the previously passed codec.* param options A dictionary filled with AVCodecContext and codec-private* options, which are set on top of the options already set in* avctx, can be NULL. On return this object will be filled with* options that were not found in the avctx codec context.** return zero on success, a negative value on error* see avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(),* av_dict_set(), av_opt_set(), av_opt_find(), avcodec_parameters_to_context()*/ int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); 4. 获取裸流的解析器上下文   AVCodecParserContext(数据) AVCodecParser(方法) AVCodecParserContext *av_parser_init(int codec_id); parser av_parser_init(codec-id); 5.打开输入文件 infile fopen(filename, rb);     if (!infile) {         fprintf(stderr, Could not open %s\n, filename);         exit(1);     } 6.打开输出文件 outfile fopen(outfilename, wb);     if (!outfile) {         av_free(codec_ctx);         exit(1);     } 7. 从 输入文件中读取数据注意技巧 由于我们读取的数据 是流因此一个一个字节的读取会比较安全 data inbuf; data_size fread(inbuf, 1, AUDIO_INBUF_SIZE, infile); 从infile 中读取读取的数据存储到 inbuf 中i并让data指向inbuf的头部指针。读取的大小为 AUDIO_INBUF_SIZE 20480//注意的是 我们给的 inbuf 的大小为 uint8_t inbuf[AUDIO_INBUF_SIZE AV_INPUT_BUFFER_PADDING_SIZE];实际上为 20480 64为什么要多一个64呢//这个在 AV_INPUT_BUFFER_PADDING_SIZE 的说明中可以看到大致意思是有些编解码器有优化会用32或者64做为一整组数据如果数据是该文件的末尾那么就需要有一个buffer那么64就比较合理//也就是说我们这时候读取的aac 文件的前20480字节 在 inbuf中 在标头 stdio.h 定义size_t fread( void *restrict buffer, size_t size, size_t count,FILE *restrict stream );从给定输入流 stream 读取至多 count 个对象到数组 buffer 中如同以对每个对象调用 size 次 fgetc 并按顺序存储结果到转译为 unsigned char 数组的 buffer 中的相继位置。流的文件位置指示器前进读取的字符数。若出现错误则流的文件位置指示器的结果值不确定。若读入部分的元素则元素值不确定。参数buffer - 指向要读取的数组中首个对象的指针 size - 每个对象的字节大小 count - 要读取的对象数 stream - 读取来源的输入文件流 返回值成功读取的对象数若出现错误或文件尾条件则可能小于 count 。若 size 或 count 为零则 fread 返回零且不进行其他动作。 8. 通过av_parser_parse2函数将读取的数据 转化成 ffmpeg可以操作的 AVPacket  这时候数据已经到了inbuf 中了也就是data指针的指向我们要经过AVCodecParserContext和AVCodecContext将 data指向的数据转换成 ffmpeg 中可以操作的avpacket ret av_parser_parse2(parser, codec_ctx, pkt-data, pkt-size,data, data_size,AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); // av_parser_parse2 函数说明将 要转化的数据第五个参数 和 要转化的数据的大小第六个参数//经过解析器和加码器 转化成 传出dada数据第三个参数 和 传出data数据大小(第四个参数)//参数1解析器上下文//参数2解码器上下文//参数3传出data数据从参数5中读取到的数据经过 解析器 和 解码器 处理后存放到这里//参数4传出data数据大小从参数5中读取到的数据经过 解析器 和 解码器 处理后的大小存放到这里//参数5要转化的数据地址//参数6要转化的数据大小//参数7: 是否pts数据//参数7: *param pts输入演示时间戳。在这里输入AV_NOPTS_VALUE//参数8: *param dts输入解码时间戳。在这里输入AV_NOPTS_VALUE//参数9: *param pos输入流中的字节位置。在这里输入 0// 从第5个参数buf中拿数据最多拿 buf_size个数据实际上要拿很多次。//返回值为每次读取的数据大小。//转化后传出的数据是 AVPacket格式因此前面会通过 av_packet_alloc 分配// int av_parser_parse2(AVCodecParserContext *s,// AVCodecContext *avctx,// uint8_t **poutbuf,// int *poutbuf_size,// const uint8_t *buf,// int buf_size,// int64_t pts,// int64_t dts,// int64_t pos); 9.数据已经到AVPacket 了那么就要将AVpacket 数据变成 AVframe数据了。 到这里需要学习和理解一下AVPacket 的知识点链接为 //这里的 我们通过 自己定义的decode 函数 将这一串串工作 包起来核心工作是 通过 avcodec_send_packet 和 avcodec_receive_frame 完成的 自己包装起来的方法 static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame,                    FILE *outfile) 核心实现是 ret avcodec_send_packet(dec_ctx, pkt); ret avcodec_receive_frame(dec_ctx, frame); 10.这时候数据已经转换成avframe了 需要将avframe数据写入本地文件并且测试 //到这里理论上 avframe 中 就有了pcm的数据了那么这个pcm的数据是什么格式的呢是几声道的呢一个avframe中存储了多少数据呢         //为什么要知道格式呢因为不同的格式存储是不一样的例如AV_SAMPLE_FMT_S16 和 AV_SAMPLE_FMT_S16P 就不一样         //一个是非平面的一个是平面的。平面和非平面的存储方式是不一样的这决定了我们如果如何将pcm数据存储起来。这里可以参考PCM的存储方式问题         //当然有几个声道也是必须知道的因为平面存储和声道有关系的。         //当然不同的加码器 解码 AAC 后的 pcm 格式也不一样ffmpeg默认带的是 aac是 对应 AV_SAMPLE_FMT_S32P 格式的fdk-aac则是 对应的AV_SAMPLE_FMT_S16         //我们这里之所以关心 pcm 的格式是啥主要的原因是我们要把pcm存储到本地然后播放测试但是pcm能播放的格式是 非planar的因此如果是planar的则存储的时候要重新排列。         //也就是说我们这里有两个问题非planar交错模式的pcm我们要怎么存储呢 planar 的pcm 如何转换成非planar交错模式然后存储呢 //需要注意的一点是planar仅仅是FFmpeg内部使用的储存模式我们实际中所使用的音频都是packed模式的也就是说我们使用FFmpeg解码出音频PCM数据后如果需要写入到输出文件应该将其转为packed模式的输出。         //为了弄清楚这个问题我们需要翻看一下前面关于pcm的相关资料然后从avframe中找到对应的 成员。 //        在avframe中    uint8_t *data[AV_NUM_DATA_POINTERS];代表了存储数据的真正位置音频和视频都是这么存储的。AV_NUM_DATA_POINTERS的值是8 //        我们可以理解为 avframe 将音频分为8个声道如果是planar模式则每个声道存储在 data[i]中。如果是交错模式则都存储在data[0] 中         //         //如果是交错模式就是这样了:LRLRLRLRLRLR...... 每一个LR 就是一样音频帧所有的数据都是存储在 avframe的第一个平面。         // 存储的位置已经有了那么存储的大小是多少呢 这就就要看 avframe 中的这个值了int linesize[AV_NUM_DATA_POINTERS];         //我们既然已经得到了avformat就可以通过avformat得到想到的值了。         // 通过 int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt); 知道是不是planar 主要用到的方法以及说明 1.得到AVSampleFormat-采样格式 对于音频先得到用的是那个 AVSampleFormat对于视频 先得到用的是那个  AVPixelFormat     /**      * format of the frame, -1 if unknown or unset      * Values correspond to enum AVPixelFormat for video frames,      * enum AVSampleFormat for audio)      */           enum AVSampleFormat avsampleformat (enum AVSampleFormat)frame-format; 2.如果是音频得到有多少声道   int nbchannels frame-ch_layout.nb_channels;    3.每个声道有多少音频样本 int nb_samples frame-nb_samples; 4.每个音频样本占多少位 int data_size av_get_bytes_per_sample(avsampleformat); 5.那么上述三者相乘 就可以得到这个 AVFrame 占了多少字节了 nbchannels * nb_samples * data_size 6.还需要判断我们当前avframe 中的数据是 交错模式 还是 planar 模式。 int isplanar av_sample_fmt_is_planar(avsampleformat); 1表示是planar 模式0表示是交错模式。 是planar 模式 还是交错模式是和我们用的解码器相关的。 对于aac 来说如果用的默认的ffmpeg 自带的 aac那么是planar模式 如果使用的是 libfdk_aac, 则是交错模式 在测试代码中我们两种都用了。 有了这些数据怎么使用呢 这就需要对于 AVFrame 这个类有一些认识了参考链接 P表示Planar平面其数据格式排列方式为 :             LLLLLLRRRRRRLLLLLLRRRRRRLLLLLLRRRRRRL...每个LLLLLLRRRRRR为一个音频帧             而不带P的数据格式即交错排列排列方式为             LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRL...每个LR为一个音频样本             播放范例   ffplay -ar 48000 -ac 2 -f f32le believe.pcm 7.以交错模式存储pcm数据 为啥要用交错模式存储pcm数据呢用planar模式不行吗 --不行原因是 planar仅仅是FFmpeg内部使用的储存模式我们实际中所使用的音频都是packed模式的也就是说我们使用FFmpeg解码出音频PCM数据后如果需要写入到输出文件应该将其转为packed模式的输出。 那么这个 planar模式有啥用呢实际上再 传输的过程中planar模式很重要后面才会用到这里先知道这玩意有用就行了。 7.1 以交错模式存储 注意我们在存储交错模式的时候存储的地方是 frame-data[0]那么要存储多少呢 如下是两种写法一种是直接使用avframe 提供的 frame-linesize[0]一种是 声道数 * 每个声道有多少个音频样本 * 每个样本占用多少个字节 用这两种方式生成了两个不同的pcm believejiaocuolinesize0.pcm 和 believejiaocuochannel_persameple.pcm。 对比发现大小不一样 于是使用ffmpeg 6.0的命令 生成一个 48000_2_s16le.pcm 发现 其大小和 believejiaocuochannel_persameple.pcm 相同 结论 就用 nbchannels * frame-nb_samples * data_size 这种方法。 另外打log将每次 nbchannels * frame-nb_samples * data_size 的大小 和 frame-linesize[0]对比发现frame-linesize[0]在不满4096的时候其实就是第一次log 和最后一次logframe-linesize[0]都比nbchannels * frame-nb_samples * data_size 大 64这说明在 AVFrame的内部实现机制上也会有一个64的buffer 这里记住这个结论使用的时候注意下万一有问题才回头看源码目前这个阶段 源码有些地方还看不明白暂时忽略 另外这两个pcm播放起来是没有问题的。 还有一点在使用ffmpeg 7.0 中的ffplay 播放的pcm的时候ffplay -ar 48000 -ac 2 -f s16le believejiaocuolinesize0.pcm 总是提示 -ac 后面的值是2有问题查看了ffmpeg 的官网也没有说这个参数会变化呀在源码中查找了一下也没有看到 有啥变化此处原因不明 不管是自己build ffmpeg 7.0 添加了libfdk-aac的源码还是ffmpeg7.0提供的full build 好的源码ffplay -ac 的参数都不行 测试播放时使用 ffmpeg 6.1.1 中的ffplay 7.2 将 planar 模式转换成交错模式然后存储 目的是将planar 模式的pcm转换成 交错模式然后 存储 //将 LLLLRRRR 变成LRLRLR的过程对于planar 模式frame-data[0]存储的是LLLLLLframe-data[1]存储的是 RRRRRR //fwrite 函数的说明是size_t fwrite( const void *restrict buffer, size_t size, size_t count, FILE *restrict stream ); //写 count 个来自给定数组 buffer 的对象到输出流stream。如同转译每个对象为 unsigned char 数组并对每个对象调用 size 次 fputc 以将那些 unsigned char 按顺序写入 stream 一般写入。文件位置指示器前进写入的字节数。for (i 0; i frame-nb_samples; i) {for (ch 0; ch dec_ctx-ch_layout.nb_channels; ch) // 交错的方式写入, 大部分float的格式输出 } 8.测试播放 我们播放pcm 数据需要知道采样率声道数采样格式 采样率 int sample_rate frame-sample_rate; 声道数 int nbchannels frame-ch_layout.nb_channels; 采样格式 enum AVSampleFormat avsampleformat (enum AVSampleFormat)frame-format; 如果使用的默认ffmpeg 的aac这个值是8对应 AV_SAMPLE_FMT_FLTP,是planar模式 如果使用的libfdk_aac,这个值是1对应 AV_SAMPLE_FMT_S16是交错模式。 当我们使用的交错模式的时候那么播放使用命令为 ffplay -ar 48000 -ac 2 -f s16le believe.pcm 如果我们是从 AV_SAMPLE_FMT_FLTP的planar 模式转换成 交错模式那么播放使用命令为 ffplay -ar 48000 -ac 2 -f f32le believe.pcm 这里-ar 48000 是log中看出来的我们在转换的时候没有进行处理。 -ac 2 也是从log 中看出来的我们在转换的时候也没有进行处理。 那么从 AV_SAMPLE_FMT_FLTP 转成 交错模式后为什么-f 是 f32le呢 这就要看ffmpeg 给我提供的  ffmpeg -sample_fmts 命令中的说明了我们看到fltp 对应的是32而我们是在windows上播放的windows上用的是le也就是小端模式因此 -f后面的参数是f32le。 name depth u8 8 s16 16 s32 32 flt 32 dbl 64 u8p 8 s16p 16 s32p 32 fltp 32 dblp 64 s64 64 s64p 64 9.额外说明 在代码中实际上还有一些技巧。 1.关于指针跳动和减去已经使用的size 2.当读入的数据过小时马上开始读取的一些优化操作看代码时需要认真研读。 11.  avcodec_send_packet、avcodec_receive_frame说明 avcodec_send_packet、avcodec_receive_frame的API是FFmpeg3版本加⼊的。为了正确 的使⽤它们有必要阅读FFmpeg的⽂档说明(请点击链接)。 以下内容摘译⾃⽂档说明 FFmpeg提供了两组函数分别⽤于编码和解码 解码avcodec_send_packet()、avcodec_receive_frame()。 解码avcodec_send_frame()、avcodec_receive_packet()。 API的设计与编解码的流程⾮常贴切建议的使⽤流程如下 1. 像以前⼀样设置并打开AVCodecContext。 2. 输⼊有效的数据 解码调⽤avcodec_send_packet()给解码器传⼊包含原始的压缩数据的AVPacket对 象。 编码调⽤ avcodec_send_frame()给编码器传⼊包含解压数据的AVFrame对象。 两种情况下推荐AVPacket和AVFrame都使⽤refcounted引⽤计数的模式否则 libavcodec可能不得不对输⼊的数据进⾏拷⻉。 3. 在⼀个循环体内去接收codec的输出即周期性地调⽤avcodec_receive_*()来接收codec 输出的数据 解码调⽤avcodec_receive_frame()如果成功会返回⼀个包含未压缩数据的 AVFrame。 编码调⽤avcodec_receive_packet()如果成功会返回⼀个包含压缩数据的 AVPacket。 反复地调⽤avcodec_receive_packet()直到返回 AVERROR(EAGAIN)或其他错误。返回 AVERROR(EAGAIN)错误表示codec需要新的输⼊来输出更多的数据。对于每个输⼊的 packet或framecodec⼀般会输出⼀个frame或packet但是也有可能输出0个或者多 于1个。 4. 流处理结束的时候需要flush冲刷 codec。因为codec可能在内部缓冲多个frame或 packet出于性能或其他必要的情况如考虑B帧的情况。 处理流程如下 调⽤avcodec_send_*()传⼊的AVFrame或AVPacket指针设置为NULL。 这将进⼊ draining mode排⽔模式。 反复地调⽤avcodec_receive_*()直到返回AVERROR_EOF该⽅法在draining mode 时不会返回AVERROR(EAGAIN)的错误除⾮你没有进⼊draining mode。 当重新开启codec时需要先调⽤ avcodec_flush_buffers()来重置codec。 说明 1. 编码或者解码刚开始的时候codec可能接收了多个输⼊的frame或packet后还没有输出 数据直到内部的buffer被填充满。上⾯的使⽤流程可以处理这种情况。 2. 理论上只有在输出数据没有被完全接收的情况调⽤avcodec_send_*()的时候才可能会发 ⽣AVERROR(EAGAIN)的错误。你可以依赖这个机制来实现区别于上⾯建议流程的处理⽅ 式⽐如每次循环都调⽤avcodec_send_*()在出现AVERROR(EAGAIN)错误的时候再 去调⽤avcodec_receive_*()。 3. 并不是所有的codec都遵循⼀个严格、可预测的数据处理流程唯⼀可以保证的是 “调⽤ avcodec_send_*()/avcodec_receive_*()返回AVERROR(EAGAIN)的时候去 avcodec_receive_*()/avcodec_send_*()会成功否则不应该返回AVERROR(EAGAIN) 的错误。”⼀般来说任何codec都不允许⽆限制地缓存输⼊或者输出。 4. 在同⼀个AVCodecContext上混合使⽤新旧API是不允许的这将导致未定义的⾏为。avcodec_send_packet 函数int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt); 作⽤⽀持将裸流数据包送给解码器 警告 输⼊的avpkt-data缓冲区必须⼤于AV_INPUT_PADDING_SIZE因为优化的字节流读取 器必须⼀次读取32或者64⽐特的数据 不能跟之前的API(例如avcodec_decode_video2)混⽤否则会返回不可预知的错误 备注 在将包发送给解码器的时候AVCodecContext必须已经通过avcodec_open2打开 参数 avctx解码上下⽂ avpkt输⼊AVPakcet.通常情况下输⼊数据是⼀个单⼀的视频帧或者⼏个完整的⾳频 帧。调⽤者保留包的原有属性解码器不会修改包的内容。解码器可能创建对包的引⽤。 如果包没有引⽤计数将拷⻉⼀份。跟以往的API不⼀样输⼊的包的数据将被完全地消耗 如果包含有多个帧要求多次调⽤avcodec_recvive_frame直到 avcodec_recvive_frame返回VERROR(EAGAIN)或AVERROR_EOF。输⼊参数可以为 NULL或者AVPacket的data域设置为NULL或者size域设置为0表示将刷新所有的包 意味着数据流已经结束了。第⼀次发送刷新会总会成功第⼆次发送刷新包是没有必要 的并且返回AVERROR_EOF,如果×××缓存了⼀些帧返回⼀个刷新包将会返回所有的 解码包 返回值 0: 表示成功 AVERROR(EAGAIN)当前状态不接受输⼊⽤户必须先使⽤avcodec_receive_frame() 读 取数据帧 AVERROR_EOF解码器已刷新不能再向其发送新包 AVERROR(EINVAL)没有打开解码器或者这是⼀个编码器或者要求刷新 AVERRO(ENOMEN)⽆法将数据包添加到内部队列。 avcodec_receive_frame函数int avcodec_receive_frame ( AVCodecContext * avctx, AVFrame * frame ) 作⽤从解码器返回已解码的输出数据。 参数 avctx: 编解码器上下⽂ frame: 获取使⽤reference-counted机制的audio或者video帧取决于解码器类型。请 注意在执⾏其他操作之前函数内部将始终先调⽤av_frame_unref(frame)。 返回值 0: 成功返回⼀个帧 AVERROR(EAGAIN): 该状态下没有帧输出需要使⽤avcodec_send_packet发送新的 packet到解码器 AVERROR_EOF: 解码器已经被完全刷新不再有输出帧 AVERROR(EINVAL): 编解码器没打开 其他0的值: 具体查看对应的错误码 12. 刷新解码器 /* 冲刷解码器 */pkt-data NULL; // 让其进入drain modepkt-size 0;decode(codec_ctx, pkt, decoded_frame, outfile); 源码demo /** * projectName 07-05-decode_audio * brief 解码音频主要的测试格式aac和mp3 * author Liao Qingfu * date 2020-01-16 */ #include stdio.h #include stdlib.h #include string.h#include libavutil/frame.h #include libavutil/mem.h#include libavcodec/avcodec.h #include libavutil/samplefmt.h #include libavformat/avformat.h#define AUDIO_INBUF_SIZE 20480 #define AUDIO_REFILL_THRESH 4096static char err_buf[128] {0}; static char* av_get_err(int errnum) {av_strerror(errnum, err_buf, 128);return err_buf; }static void print_sample_format(const AVFrame *frame) {printf(ar-samplerate: %uHz\n, frame-sample_rate);printf(ac-channel: %u\n, frame-ch_layout.nb_channels);printf(f-format: %u\n, frame-format);// 格式需要注意实际存储到本地文件时已经改成交错模式 }//这里的 pkt 中存储的是从 infile 读取到的数据通过 avcodec_send_packet 将avpacket的数据发送到 AVCodecContext //AVCodecContext内部会处理将avpacket 转化成 avframe static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame,FILE *outfile) {int i, ch;int ret, data_size;/* send the packet with the compressed data to the decoder *///返回值为0表示发送成功如果为EAGAIN 表示要重新读如果是其他error说明该方法调用有问题ret avcodec_send_packet(dec_ctx, pkt);if(ret AVERROR(EAGAIN)){fprintf(stderr, Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n);}else if (ret 0){fprintf(stderr, Error submitting the packet to the decoder, err:%s, pkt_size:%d\n,av_get_err(ret), pkt-size); // exit(1);return;}/* read all the output frames (infile general there may be any number of them */while (ret 0){// 对于frame, avcodec_receive_frame内部每次都先调用ret avcodec_receive_frame(dec_ctx, frame);if (ret AVERROR(EAGAIN) || ret AVERROR_EOF)return;else if (ret 0){fprintf(stderr, Error during decoding\n);exit(1);}//到这里理论上 frame 中 就有了pcm的数据了那么这个pcm的数据是什么格式的呢是几声道的呢一个音频帧有多少个字节呢//为什么要知道格式呢因为不同的格式存储是不一样的例如AV_SAMPLE_FMT_S16 和 AV_SAMPLE_FMT_S16P 就不一样//一个是非平面的一个是平面的。平面和非平面的存储方式是不一样的这决定了我们如果如何将pcm数据存储起来。这里可以参考PCM的存储方式问题//当然有几个声道也是必须知道的因为平面存储和声道有关系的。//当然不同的加码器 解码 AAC 后的 pcm 格式也不一样ffmpeg默认带的是 aac是 对应 AV_SAMPLE_FMT_S32P 格式的fdk-aac则是 对应的AV_SAMPLE_FMT_S16//我们这里之所以关心 pcm 的格式是啥主要的原因是我们要把pcm存储到本地然后播放测试但是pcm能播放的格式是 非planar的因此如果是planar的则存储的时候要重新排列。//也就是说我们这里有两个问题非planar交错模式的pcm我们要怎么存储呢 planar 的pcm 如何转换成非planar交错模式然后存储呢 //需要注意的一点是planar仅仅是FFmpeg内部使用的储存模式我们实际中所使用的音频都是packed模式的也就是说我们使用FFmpeg解码出音频PCM数据后如果需要写入到输出文件应该将其转为packed模式的输出。//为了弄清楚这个问题我们需要翻看一下前面关于pcm的相关资料然后从avframe中找到对应的 成员。 // 在avframe中 uint8_t *data[AV_NUM_DATA_POINTERS];代表了存储数据的真正位置音频和视频都是这么存储的。AV_NUM_DATA_POINTERS的值是8 // 我们可以理解为 avframe 将音频分为8个声道如果是planar模式则每个声道存储在 data[i]中。如果是交错模式则都存储在data[0] 中////如果是交错模式就是这样了:LRLRLRLRLRLR...... 每一个LR 就是一样音频帧所有的数据都是存储在 avframe的第一个平面。// 存储的位置已经有了那么存储的大小是多少呢 这就就要看 avframe 中的这个值了int linesize[AV_NUM_DATA_POINTERS];//我们既然已经得到了avformat就可以通过avformat得到想到的值了。// 通过 int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt); 知道是不是planarenum AVSampleFormat avsampleformat (enum AVSampleFormat)frame-format;printf(avsampleformat %d\n,avsampleformat);int linesize0 frame-linesize[0]; // printf(linesize0 %d\n,linesize0);int nbchannels frame-ch_layout.nb_channels; // printf(nbchannels %d\n,nbchannels);int sample_rate frame-sample_rate;printf(sample_rate %d\n,sample_rate); // 48000int nb_samples frame-nb_samples;printf(nb_samples %d\n,nb_samples); //1024 实际上就是一个aac 的avframe应该有1024个样本int isplanar av_sample_fmt_is_planar(avsampleformat); // printf(isplanar %d\n,isplanar);data_size av_get_bytes_per_sample(dec_ctx-sample_fmt); // printf(111 dec_ctx-sample_fmt %d\n,dec_ctx-sample_fmt); // printf(data_size %d\n,data_size);if (data_size 0){/* This should not occur, checking just for paranoia */fprintf(stderr, Failed to calculate data size\n);exit(1);}static int s_print_format 0;//根据自己在该方法前面加的log打印就会明白这里为什么要有一个 static int s_print_format因为这个方法会不停的走进来打印的太多了if(s_print_format 0){s_print_format 1;print_sample_format(frame);}/**P表示Planar平面其数据格式排列方式为 :LLLLLLRRRRRRLLLLLLRRRRRRLLLLLLRRRRRRL...每个LLLLLLRRRRRR为一个音频帧而不带P的数据格式即交错排列排列方式为LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRL...每个LR为一个音频样本播放范例 ffplay -ar 48000 -ac 2 -f f32le believe.pcm*/if(isplanar){static int s_print_format111 0;//根据自己在该方法前面加的log打印就会明白这里为什么要有一个 static int s_print_format因为这个方法会不停的走进来打印的太多了if(s_print_format111 0){s_print_format111 1;printf(isplanner insert data ... \n);}//将 LLLLRRRR 变成LRLRLR的过程对于planar 模式frame-data[0]存储的是LLLLLLframe-data[1]存储的是 RRRRRR//fwrite 函数的说明是size_t fwrite( const void *restrict buffer, size_t size, size_t count, FILE *restrict stream );//写 count 个来自给定数组 buffer 的对象到输出流stream。如同转译每个对象为 unsigned char 数组并对每个对象调用 size 次 fputc 以将那些 unsigned char 按顺序写入 stream 一般写入。文件位置指示器前进写入的字节数。for (i 0; i frame-nb_samples; i){for (ch 0; ch dec_ctx-ch_layout.nb_channels; ch) // 交错的方式写入, 大部分float的格式输出fwrite(frame-data[ch] data_size*i, 1, data_size, outfile);}} else {static int s_print_format222 0;//根据自己在该方法前面加的log打印就会明白这里为什么要有一个 static int s_print_format因为这个方法会不停的走进来打印的太多了if(s_print_format222 0){s_print_format222 1;printf(not isplanner insert data ... \n);}//注意我们在存储交错模式的时候存储的地方是 frame-data[0]那么要存储多少呢//如下是两种写法一种是直接使用avframe 提供的 frame-linesize[0]//一种是 声道数 * 每个声道有多少个音频样本 * 每个样本占用多少个字节//用这两种方式生成了两个不同的pcmbelievejiaocuolinesize0.pcm 和 believejiaocuochannel_persameple.pcm 对比发现大小不一样//于是使用ffmpeg 6.0的命令 生成一个 48000_2_s16le.pcm发现 其大小和 believejiaocuochannel_persameple.pcm 相同//结论 就用 nbchannels * frame-nb_samples * data_size 这种方法。//另外打log将每次 nbchannels * frame-nb_samples * data_size 的大小 和 frame-linesize[0]对比发现frame-linesize[0]在不满4096的时候其实就是第一次log 和最后一次logframe-linesize[0]都比nbchannels * frame-nb_samples * data_size 大 64这说明在 AVFrame的内部实现机制上也会有一个64的buffer//这里记住这个结论使用的时候注意下万一有问题才回头看源码目前这个阶段 源码有些地方还看不明白暂时忽略//这两个pcm播放起来是没有问题的。//还有一点在使用ffmpeg 7.0 中的ffplay 播放的pcm的时候ffplay -ar 48000 -ac 2 -f s16le believejiaocuolinesize0.pcm // 总是提示 -ac 后面的值是2有问题查看了ffmpeg 的官网也没有说这个参数会变化呀在源码中查找了一下也没有看到 有啥变化此处原因不明//不管是自己build ffmpeg 7.0 添加了libfdk-aac的源码还是ffmpeg7.0提供的full build 好的源码ffplay -ac 的参数都不行//测试播放时使用 ffmpeg 6.1.1 中的ffplay//fwrite(frame-data[0], 1, frame-linesize[0], outfile);fwrite(frame-data[0], 1, nbchannels * frame-nb_samples * data_size, outfile);static int s_print_format333 0;//根据自己在该方法前面加的log打印就会明白这里为什么要有一个 static int s_print_format因为这个方法会不停的走进来打印的太多了 // if(s_print_format333 0) // {s_print_format333 1;printf(not isplanner insert data nbchannels * frame-nb_samples * data_size %d ... \n,nbchannels * frame-nb_samples * data_size);printf(frame-line[0] %d\n,frame-linesize[0]); // }}} } // 播放范例 ffplay -ar 48000 -ac 2 -f f32le believe.pcm int main(int argc, char **argv) {//输出文件的名字const char *outfilename;//输入文件的名字const char *filename;//1.解码器const AVCodec *codec;//2.解码器上下文AVCodecContext *codec_ctx NULL;//3.解析器上下文解码的时候要用到这个。AVCodecParserContext *parser NULL;int len 0;int ret 0;FILE *infile NULL;FILE *outfile NULL;uint8_t inbuf[AUDIO_INBUF_SIZE AV_INPUT_BUFFER_PADDING_SIZE];uint8_t *data NULL;size_t data_size 0;AVPacket *pkt NULL;AVFrame *decoded_frame NULL;//我们这里直接指定文件不使用参数传递的形式 // if (argc 2) // { // fprintf(stderr, Usage: %s input file output file\n, argv[0]); // exit(0); // } // filename argv[1]; // outfilename argv[2];filename D:/AllInformation/qtworkspacenew/07-05-decode_audio/believe.aac;// outfilename D:/AllInformation/qtworkspacenew/07-05-decode_audio/believejiaocuolinesize0.pcm;outfilename D:/AllInformation/qtworkspacenew/07-05-decode_audio/believejiaocuochannel_persameple.pcm;pkt av_packet_alloc();enum AVCodecID audio_codec_id AV_CODEC_ID_AAC;if(strstr(filename, aac) ! NULL){audio_codec_id AV_CODEC_ID_AAC;}else if(strstr(filename, mp3) ! NULL){audio_codec_id AV_CODEC_ID_MP3;}else{printf(default codec id:%d\n, audio_codec_id);}// 1.查找解码器 AVCodec // codec avcodec_find_decoder(audio_codec_id); // AV_CODEC_ID_AACcodec avcodec_find_decoder_by_name(libfdk_aac);if (!codec) {fprintf(stderr, Codec not found\n);exit(1);}printf(编解码器id codec-id %d\n,codec-id);//86018 AV_CODEC_ID_AAC,printf(编解码器的name codec-name %s\n,codec-name);//aacprintf(编解码器的long name codec-long_name %s\n,codec-long_name);//AAC (Advanced Audio Coding)printf(编解码器的类型 codec-type %d\n,codec-type);//1,对应的是AVMediaType 是 AVMEDIA_TYPE_AUDIOprintf(解码支持的低分辨率的最大值 codec-max_lowres %d\n,codec-max_lowres); //由于当前测试的aac是声音因此这个值是0printf(编解码器功能codec-capabilities %d\n,codec-capabilities);//0100 0000 0010 对应如下两个 | 起来 #define AV_CODEC_CAP_CHANNEL_CONF (1 10) #define AV_CODEC_CAP_DR1 (1 1)//const AVRational *supported_framerates; /// array of supported framerates, or NULL if any, array is terminated by {0,0}//AVRational 是一个分子分母的结构对于音频来说就是采样率每秒中采集的样本数量 对于视频来说就是每秒中播放多少帧。if (codec-supported_frameratesNULL) {printf(编解码器支持的帧速率阵列 codec-supported_framerates null\n); //结果为codec-supported_framerates null} else {int i 0;while(1){if(codec-supported_framerates[i].den 0 codec-supported_framerates[i].num 0){break;}else {printf(编解码器支持的帧速率阵列 codec-supported_framerates[%d].den %d,codec-supported_framerates[%d].num %d\n,i,codec-supported_framerates[i].den,i,codec-supported_framerates[i].num);i;}}}//const enum AVPixelFormat *pix_fmts; /// array of supported pixel formats, or NULL if unknown, array is terminated by -1//AVPixelFormat,AV_PIX_FMT_YUV420P对应的是视频的格式是YUVP的还是RGB888的if (codec-pix_fmtsNULL) {printf(codec-pix_fmts null\n); //结果为codec-pix_fmts null} else {int i 0;while(1){if(codec-pix_fmts[i] -1){break;}else{printf(codec-pix_fmts[%d] %d\n,i,codec-pix_fmts[i]);i;}}}//const enum AVSampleFormat *sample_fmts; /// array of supported sample formats, or NULL if unknown, array is terminated by -1// AVSampleFormat AV_SAMPLE_FMT_S16, 对应的是音频的 采样格式if (codec-sample_fmtsNULL) {printf(codec-sample_fmts null\n);} else {int i 0;while(1){if(codec-sample_fmts[i] -1){break;}else{printf(codec-sample_fmts[%d] %d\n,i,codec-sample_fmts[i]);//结果为codec-sample_fmts[0] 88对应的是AV_SAMPLE_FMT_FLTP /// float, planari;}}}//const int *supported_samplerates; /// array of supported audio samplerates, or NULL if unknown, array is terminated by 0//支持的音频采样率阵列if (codec-supported_sampleratesNULL) {printf(codec-supported_samplerates null\n); //结果为codec-supported_samplerates null} else {int i 0;while(1){if(codec-supported_samplerates[i] 0){break;}else{printf(codec-supported_samplerates[%d] %d\n,i,codec-supported_samplerates[i]);i;}}}//const AVClass *priv_class; /// AVClass for the private context//这个 AVClass 暂时不知道在哪里用到。因此暂时不打印log//const AVProfile *profiles; /// array of recognized profiles, or NULL if unknown, array is terminated by {AV_PROFILE_UNKNOWN} //已识别的配置文件阵列,这里理解为 当前编解码器应该有多种编解码方式比如AAC 有 LC模式MAIN模式等if (codec-profilesNULL) {printf(codec-profiles null\n); //这时候还是null} else {int i 0;while(1){if(codec-profiles[i].profile AV_PROFILE_UNKNOWN){break;}else{printf(codec-profiles[%d].name %s\n,i,codec-profiles[i].name);printf(codec-profiles[%d].profile %d\n,i,codec-profiles[i].profile);i;}}}//const char *wrapper_name;if (codec-wrapper_nameNULL) {printf(codec-wrapper_name null\n); //这时候还是null} else {printf(codec-wrapper_name %s\n,codec-wrapper_name);}// /** // * Array of supported channel layouts, terminated with a zeroed layout. // */ // const AVChannelLayout *ch_layouts;//当前编解码器支持的 声道数量if (codec-ch_layoutsNULL) {printf(codec-ch_layouts null\n);} else {int i 0;while(1){if(codec-ch_layouts[i].nb_channels0){break;}else{printf(codec-ch_layouts[i].nb_channels %d\n,i,codec-ch_layouts[i].nb_channels);i;}}}printf(1111111\n);// 2.分配codec上下文 AVCodecContextcodec_ctx avcodec_alloc_context3(codec);if (!codec_ctx) {fprintf(stderr, Could not allocate audio codec context\n);exit(1);}printf(2222\n);// 3.将解码器和解码器上下文进行关联if (avcodec_open2(codec_ctx, codec, NULL) 0) {fprintf(stderr, Could not open codec\n);exit(1);}printf(33333333\n);// 4.获取裸流的解析器 AVCodecParserContext(数据) AVCodecParser(方法)parser av_parser_init(codec-id);if (!parser) {fprintf(stderr, Parser not found\n);exit(1);}printf(44444444\n);// 5.打开输入文件infile fopen(filename, rb);if (!infile) {fprintf(stderr, Could not open %s\n, filename);exit(1);}// 6.打开输出文件outfile fopen(outfilename, wb);if (!outfile) {av_free(codec_ctx);exit(1);}// 7.读取文件进行解码从infile 中读取读取的数据存储到 inbuf 中i并让data指向inbuf的头部指针。读取的大小为 AUDIO_INBUF_SIZE 20480//注意的是 我们给的 inbuf 的大小为 uint8_t inbuf[AUDIO_INBUF_SIZE AV_INPUT_BUFFER_PADDING_SIZE];实际上为 20480 64为什么要多一个64呢//这个在 AV_INPUT_BUFFER_PADDING_SIZE 的说明中可以看到大致意思是有些编解码器有优化会用32或者64做为一整组数据如果数据是该文件的末尾那么就需要有一个buffer那么64就比较合理//也就是说我们这时候读取的aac 文件的前20480字节 在 inbuf中data inbuf;data_size fread(inbuf, 1, AUDIO_INBUF_SIZE, infile);while (data_size 0){if (!decoded_frame){if (!(decoded_frame av_frame_alloc())){fprintf(stderr, Could not allocate audio frame\n);exit(1);}}// av_parser_parse2 函数说明将 要转化的数据第五个参数 和 要转化的数据的大小第六个参数//经过解析器和加码器 转化成 传出dada数据第三个参数 和 传出data数据大小(第四个参数)//参数1解析器上下文//参数2解码器上下文//参数3传出data数据从参数5中读取到的数据经过 解析器 和 解码器 处理后存放到这里//参数4传出data数据大小从参数5中读取到的数据经过 解析器 和 解码器 处理后的大小存放到这里//参数5要转化的数据地址//参数6要转化的数据大小//参数7: 是否pts数据//参数7: *param pts输入演示时间戳。在这里输入AV_NOPTS_VALUE//参数8: *param dts输入解码时间戳。在这里输入AV_NOPTS_VALUE//参数9: *param pos输入流中的字节位置。在这里输入 0// 从第5个参数buf中拿数据最多拿 buf_size个数据实际上要拿很多次。//返回值为每次读取的数据大小。//转化后传出的数据是 AVPacket格式因此前面会通过 av_packet_alloc 分配// int av_parser_parse2(AVCodecParserContext *s,// AVCodecContext *avctx,// uint8_t **poutbuf,// int *poutbuf_size,// const uint8_t *buf,// int buf_size,// int64_t pts,// int64_t dts,// int64_t pos);ret av_parser_parse2(parser, codec_ctx, pkt-data, pkt-size,data, data_size,AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if (ret 0){fprintf(stderr, Error while parsing\n);exit(1);}data ret; // 跳过已经解析的数据data_size - ret; // 对应的缓存大小也做相应减小//经过av_parser_parse2后真正的数据这时候已经在pkt 中了因此要将pkt中的数据处理成 pcm数据。通过自己写的decode方法完成解码后的数据就是 原始数据了在ffmpeg中通过 AVFrame存储因此这里要存储到 AVFrame中if (pkt-size)decode(codec_ctx, pkt, decoded_frame, outfile);if (data_size AUDIO_REFILL_THRESH) // 如果数据少了则再次读取{memmove(inbuf, data, data_size); // 把之前剩的数据拷贝到buffer的起始位置data inbuf;// 读取数据 长度: AUDIO_INBUF_SIZE - data_sizelen fread(data data_size, 1, AUDIO_INBUF_SIZE - data_size, infile);if (len 0)data_size len;}}/* 冲刷解码器 */pkt-data NULL; // 让其进入drain modepkt-size 0;decode(codec_ctx, pkt, decoded_frame, outfile);fclose(outfile);fclose(infile);avcodec_free_context(codec_ctx);av_parser_close(parser);av_frame_free(decoded_frame);av_packet_free(pkt);printf(main finish, please enter Enter and exit\n);return 0; }
文章转载自:
http://www.morning.srtw.cn.gov.cn.srtw.cn
http://www.morning.qgfkn.cn.gov.cn.qgfkn.cn
http://www.morning.ltxgk.cn.gov.cn.ltxgk.cn
http://www.morning.knmp.cn.gov.cn.knmp.cn
http://www.morning.rbkdg.cn.gov.cn.rbkdg.cn
http://www.morning.rdtq.cn.gov.cn.rdtq.cn
http://www.morning.ndynz.cn.gov.cn.ndynz.cn
http://www.morning.qlznd.cn.gov.cn.qlznd.cn
http://www.morning.elmtw.cn.gov.cn.elmtw.cn
http://www.morning.gbwfx.cn.gov.cn.gbwfx.cn
http://www.morning.krhkn.cn.gov.cn.krhkn.cn
http://www.morning.qhrlb.cn.gov.cn.qhrlb.cn
http://www.morning.wkkqw.cn.gov.cn.wkkqw.cn
http://www.morning.xbhpm.cn.gov.cn.xbhpm.cn
http://www.morning.tbnpn.cn.gov.cn.tbnpn.cn
http://www.morning.srnth.cn.gov.cn.srnth.cn
http://www.morning.ptdzm.cn.gov.cn.ptdzm.cn
http://www.morning.dqkcn.cn.gov.cn.dqkcn.cn
http://www.morning.pccqr.cn.gov.cn.pccqr.cn
http://www.morning.ygmw.cn.gov.cn.ygmw.cn
http://www.morning.zlkps.cn.gov.cn.zlkps.cn
http://www.morning.fchkc.cn.gov.cn.fchkc.cn
http://www.morning.wdjcr.cn.gov.cn.wdjcr.cn
http://www.morning.mbaiwan.com.gov.cn.mbaiwan.com
http://www.morning.nrll.cn.gov.cn.nrll.cn
http://www.morning.mwrxz.cn.gov.cn.mwrxz.cn
http://www.morning.zydr.cn.gov.cn.zydr.cn
http://www.morning.tlfyb.cn.gov.cn.tlfyb.cn
http://www.morning.pcgmw.cn.gov.cn.pcgmw.cn
http://www.morning.ppghc.cn.gov.cn.ppghc.cn
http://www.morning.mdnnz.cn.gov.cn.mdnnz.cn
http://www.morning.cbndj.cn.gov.cn.cbndj.cn
http://www.morning.rfpb.cn.gov.cn.rfpb.cn
http://www.morning.nmtyx.cn.gov.cn.nmtyx.cn
http://www.morning.zlrsy.cn.gov.cn.zlrsy.cn
http://www.morning.bpmmq.cn.gov.cn.bpmmq.cn
http://www.morning.ktrzt.cn.gov.cn.ktrzt.cn
http://www.morning.cbnjt.cn.gov.cn.cbnjt.cn
http://www.morning.qjlkp.cn.gov.cn.qjlkp.cn
http://www.morning.hknk.cn.gov.cn.hknk.cn
http://www.morning.ghrhb.cn.gov.cn.ghrhb.cn
http://www.morning.fldk.cn.gov.cn.fldk.cn
http://www.morning.trrpb.cn.gov.cn.trrpb.cn
http://www.morning.rxhsm.cn.gov.cn.rxhsm.cn
http://www.morning.fbdtd.cn.gov.cn.fbdtd.cn
http://www.morning.ynlpy.cn.gov.cn.ynlpy.cn
http://www.morning.zlgth.cn.gov.cn.zlgth.cn
http://www.morning.gswfs.cn.gov.cn.gswfs.cn
http://www.morning.yprnp.cn.gov.cn.yprnp.cn
http://www.morning.fnfhs.cn.gov.cn.fnfhs.cn
http://www.morning.gxtfk.cn.gov.cn.gxtfk.cn
http://www.morning.clbzy.cn.gov.cn.clbzy.cn
http://www.morning.wphzr.cn.gov.cn.wphzr.cn
http://www.morning.bsqth.cn.gov.cn.bsqth.cn
http://www.morning.bpds.cn.gov.cn.bpds.cn
http://www.morning.swwpl.cn.gov.cn.swwpl.cn
http://www.morning.zfyr.cn.gov.cn.zfyr.cn
http://www.morning.pnmnl.cn.gov.cn.pnmnl.cn
http://www.morning.smygl.cn.gov.cn.smygl.cn
http://www.morning.wwkdh.cn.gov.cn.wwkdh.cn
http://www.morning.c-ae.cn.gov.cn.c-ae.cn
http://www.morning.fmkjx.cn.gov.cn.fmkjx.cn
http://www.morning.dybth.cn.gov.cn.dybth.cn
http://www.morning.pqqzd.cn.gov.cn.pqqzd.cn
http://www.morning.jppb.cn.gov.cn.jppb.cn
http://www.morning.pkmcr.cn.gov.cn.pkmcr.cn
http://www.morning.sgrwd.cn.gov.cn.sgrwd.cn
http://www.morning.qmbpy.cn.gov.cn.qmbpy.cn
http://www.morning.pzss.cn.gov.cn.pzss.cn
http://www.morning.jtkfm.cn.gov.cn.jtkfm.cn
http://www.morning.bzwxr.cn.gov.cn.bzwxr.cn
http://www.morning.kmqlf.cn.gov.cn.kmqlf.cn
http://www.morning.drcnn.cn.gov.cn.drcnn.cn
http://www.morning.bttph.cn.gov.cn.bttph.cn
http://www.morning.rpwm.cn.gov.cn.rpwm.cn
http://www.morning.kbntl.cn.gov.cn.kbntl.cn
http://www.morning.nmbbt.cn.gov.cn.nmbbt.cn
http://www.morning.kfwrq.cn.gov.cn.kfwrq.cn
http://www.morning.hjrjy.cn.gov.cn.hjrjy.cn
http://www.morning.mcpdn.cn.gov.cn.mcpdn.cn
http://www.tj-hxxt.cn/news/237289.html

相关文章:

  • mc做弊端网站金州网站建设
  • 购物网站的推广摄影作品投稿网站
  • wordpress之家aso优化软件
  • 响应式企业网站系统如何制作史莱姆 简单
  • 网站模板后台怎么做建设网站比较好
  • 软件外包公司联系方式seo推广是什么工作
  • 做网站后要回源码有何用群晖外网访问wordpress时格式变完
  • 顺德网站设计制作免费咨询医生回答
  • 研究院网站模板二手书屋网站开发的意义
  • 网站首页页面设计漳州网站建设公司首选公司
  • 如何免费推广自己的网站能不能自己做网站推广
  • 网站制作开发及优化是什么网站为何要屏蔽百度蜘蛛
  • 南京做网站优化价格贵阳做网站公司排名
  • 网站自助授权系统朔州网站建设电话
  • 怎么开个人网站赚钱建筑工人找活的平台app
  • 开发个网站多少钱广州网站建设推广公司哪家好
  • 企业网站推广的首选办法是郑州网站推广优化
  • 河北建设集团有限公司网站软件技术培训机构
  • 网站优化方案基本流程怎么用云服务器建设网站
  • 怎么做销售网站一个服务器怎么做两个网站
  • 网页站点wordpress 分类 如何修改
  • 厦门建网站的公司宜城市城乡建设局网站
  • 哈尔滨网站运营服务商华创网站建设
  • 免费网站推广群发软件青岛谁优化网站做的好处
  • 移动网站建设公司遵义企业做网站
  • 网站建设与管理实践报告总结手机网址打不开怎么解决
  • 现在有没有免费的网站空间闵行区网站开发
  • 用vs2010做网站的好处汉中网站设计
  • 哇塞fm网站维护人人秀h5制作软件
  • 网站项目报价方案网站建设合同要注意什么