音视频实战--音视频编码

news/2025/3/14 17:18:53/

1、查找所需的编码器–avcodec_find_encoder或avcodec_find_encoder_by_name

音频编码和视频编码流程基本相同,使用音频编码器则可以编码音频数据,使用视频编码器则可以编码视频数据。

/* 指定的编码器 ID 查找对应的编码器。可以通过这个函数来获取特定编码器的 AVCodec 结构体,从而进行后续的编码操作。* id:表示要查找的编码器的 AVCodecID 枚举类型标识符。*/ 
AVCodec *avcodec_find_encoder(enum AVCodecID id);/* 根据指定的编码器名称查找对应的编码器。*/
AVCodec *avcodec_find_encoder_by_name(const char *name);

2、获取编码上下文信息存储结构–avcodec_alloc_context3

/* 该函数将返回一个指向 AVCodecContext 结构体的指针,该结构体已经被适当地分配和初始化,可以在接下来的编解码操作中使用。* codec:指向要分配上下文的 AVCodec 结构体的指针。*/ 
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);

3、设置编码上下文信息

4、将编码器上下文和编码器进行关联—avcodec_open2

/* 该函数会尝试打开指定的编解码器,并将其状态保存在 AVCodecContext 结构体中,以便后续进行编解码操作。如果打开成功,函数将返回0;如果发生错误,将返回一个负值表示错误代码。
* avctx:指向要打开的 AVCodecContext 结构体的指针。
* codec:指向要使用的 AVCodec 结构体的指针。
* options:一个指向 AVDictionary 指针的指针,用于传递额外的选项参数给编解码器。
*/ 
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

5、分配存储压缩编码数据的帧结构–av_packet_alloc

/*分配一个新的 AVPacket 结构体,并对其进行初始化。AVPacket 结构体用于存储压缩编码后的音视频数据包,可以在音视频编解码过程中用来传递数据。*/ 
AVPacket *av_packet_alloc(void);

6、分配存储音视频帧元数据结构–av_frame_alloc

/* 分配一个新的 AVFrame 结构体,并对其进行初始化。AVFrame 结构体用于存储音视频帧的数据,包括像素数据、采样数据以及相关的元信息。
*/
AVFrame *av_frame_alloc(void);

7、将数据填充的AVFrame 结构中—av_samples_fill_arrays

/* 将已分配的内存填充为包含音频样本数据的数组。这个函数通常用于为音频数据提供一个包含样本数据的数组,以便后续进行音频编解码或处理操作。* audio_data:指向一个指针数组的指针,用于存储填充后的音频数据。* linesize:一个整型数组,用于存储每个音频通道的行大小(字节数),如果不需要可以传入 NULL。* buf:指向包含音频数据的缓冲区。* nb_channels:音频通道数。* nb_samples:每个通道中的样本数。* sample_fmt:音频采样格式,枚举类型 AVSampleFormat。* align:对齐值,通常设为0。*/int av_samples_fill_arrays(uint8_t **audio_data, int *linesize,const uint8_t *buf, int nb_channels, int nb_samples,enum AVSampleFormat sample_fmt, int align);

8、将音频或视频源数据发送到编解码中处理–avcodec_send_frame

/* 该函数将会把待编解码的帧送入编解码器进行处理。如果发送成功,函数将返回0;如果发生错误,将返回一个负值表示错误代码。* avctx:指向已经打开的 AVCodecContext 结构体的指针,表示要使用的编解码器上下文。* frame:指向 AVFrame 结构体的指针,表示待编解码的音视频帧数据。*/ 
int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);

9、接收编码后的数据包–avcodec_receive_packet

/* 该函数将从编解码器中接收解码后的数据包,并将其存储在提供的 AVPacket 结构体中。如果成功接收到数据包,则函数返回0;如果没有可用数据包或者出现错误,则返回负值表示错误代码。* avctx:指向已经打开的 AVCodecContext 结构体的指针,表示要使用的编解码器上下文。* avpkt:指向 AVPacket 结构体的指针,用于接收解码后的数据包。*/ 
int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);

10、将编码后的音频、视频数据包按照一定的格式存储到文件中

例如:AAC文件,则先写好其adts头部数据再存储音频数据帧。

11、音频代码

/**
* @projectName   08-01-encode_audio
* @brief         音频编码
*               从本地读取PCM数据进行AAC编码
*           1. 输入PCM格式问题,通过AVCodec的sample_fmts参数获取具体的格式支持
*           (1)默认的aac编码器输入的PCM格式为:AV_SAMPLE_FMT_FLTP
*           (2)libfdk_aac编码器输入的PCM格式为AV_SAMPLE_FMT_S16.
*           2. 支持的采样率,通过AVCodec的supported_samplerates可以获取
* @author        Liao Qingfu
* @date          2020-04-15
*/#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>#include <libavcodec/avcodec.h>#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
#include <libavutil/opt.h>/* 检测该编码器是否支持该采样格式 */
static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt)
{const enum AVSampleFormat *p = codec->sample_fmts;while (*p != AV_SAMPLE_FMT_NONE) { // 通过AV_SAMPLE_FMT_NONE作为结束符if (*p == sample_fmt)return 1;p++;}return 0;
}/* 检测该编码器是否支持该采样率 */
static int check_sample_rate(const AVCodec *codec, const int sample_rate)
{const int *p = codec->supported_samplerates;while (*p != 0)  {// 0作为退出条件,比如libfdk-aacenc.c的aac_sample_ratesprintf("%s support %dhz\n", codec->name, *p);if (*p == sample_rate)return 1;p++;}return 0;
}/* 检测该编码器是否支持该采样率, 该函数只是作参考 */
static int check_channel_layout(const AVCodec *codec, const uint64_t channel_layout)
{// 不是每个codec都给出支持的channel_layoutconst uint64_t *p = codec->channel_layouts;if(!p) {printf("the codec %s no set channel_layouts\n", codec->name);return 1;}while (*p != 0) { // 0作为退出条件,比如libfdk-aacenc.c的aac_channel_layoutprintf("%s support channel_layout %d\n", codec->name, *p);if (*p == channel_layout)return 1;p++;}return 0;
}static void get_adts_header(AVCodecContext *ctx, uint8_t *adts_header, int aac_length)
{uint8_t freq_idx = 0;    //0: 96000 Hz  3: 48000 Hz 4: 44100 Hzswitch (ctx->sample_rate) {case 96000: freq_idx = 0; break;case 88200: freq_idx = 1; break;case 64000: freq_idx = 2; break;case 48000: freq_idx = 3; break;case 44100: freq_idx = 4; break;case 32000: freq_idx = 5; break;case 24000: freq_idx = 6; break;case 22050: freq_idx = 7; break;case 16000: freq_idx = 8; break;case 12000: freq_idx = 9; break;case 11025: freq_idx = 10; break;case 8000: freq_idx = 11; break;case 7350: freq_idx = 12; break;default: freq_idx = 4; break;}uint8_t chanCfg = ctx->channels;uint32_t frame_length = aac_length + 7;adts_header[0] = 0xFF;adts_header[1] = 0xF1;adts_header[2] = ((ctx->profile) << 6) + (freq_idx << 2) + (chanCfg >> 2);adts_header[3] = (((chanCfg & 3) << 6) + (frame_length  >> 11));adts_header[4] = ((frame_length & 0x7FF) >> 3);adts_header[5] = (((frame_length & 7) << 5) + 0x1F);adts_header[6] = 0xFC;
}
/*
*
*/
static int encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, FILE *output)
{int ret;/* send the frame for encoding */ret = avcodec_send_frame(ctx, frame);if (ret < 0) {fprintf(stderr, "Error sending the frame to the encoder\n");return -1;}/* read all the available output packets (in general there may be any number of them */// 编码和解码都是一样的,都是send 1次,然后receive多次, 直到AVERROR(EAGAIN)或者AVERROR_EOFwhile (ret >= 0) {ret = avcodec_receive_packet(ctx, pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return 0;} else if (ret < 0) {fprintf(stderr, "Error encoding audio frame\n");return -1;}uint8_t aac_header[7];get_adts_header(ctx, aac_header, pkt->size);size_t len = 0;len = fwrite(aac_header, 1, 7, output);if(len != 7) {fprintf(stderr, "fwrite aac_header failed\n");return -1;}len = fwrite(pkt->data, 1, pkt->size, output);if(len != pkt->size) {fprintf(stderr, "fwrite aac data failed\n");return -1;}/* 是否需要释放数据? avcodec_receive_packet第一个调用的就是 av_packet_unref* 所以我们不用手动去释放,这里有个问题,不能将pkt直接插入到队列,因为编码器会释放数据* 可以新分配一个pkt, 然后使用av_packet_move_ref转移pkt对应的buffer*/// av_packet_unref(pkt);}return -1;
}/** 这里只支持2通道的转换
*/
void f32le_convert_to_fltp(float *f32le, float *fltp, int nb_samples) {float *fltp_l = fltp;   // 左通道float *fltp_r = fltp + nb_samples;   // 右通道for(int i = 0; i < nb_samples; i++) {fltp_l[i] = f32le[i*2];     // 0 1   - 2 3fltp_r[i] = f32le[i*2+1];   // 可以尝试注释左声道或者右声道听听声音}
}
/** 提取测试文件:* (1)s16格式:ffmpeg -i buweishui.aac -ar 48000 -ac 2 -f s16le 48000_2_s16le.pcm* (2)flt格式:ffmpeg -i buweishui.aac -ar 48000 -ac 2 -f f32le 48000_2_f32le.pcm*      ffmpeg只能提取packed格式的PCM数据,在编码时候如果输入要为fltp则需要进行转换* 测试范例:* (1)48000_2_s16le.pcm libfdk_aac.aac libfdk_aac  // 如果编译的时候没有支持fdk aac则提示找不到编码器* (2)48000_2_f32le.pcm aac.aac aac // 我们这里只测试aac编码器,不测试fdkaac
*/
int main(int argc, char **argv)
{char *in_pcm_file = NULL;char *out_aac_file = NULL;FILE *infile = NULL;FILE *outfile = NULL;const AVCodec *codec = NULL;AVCodecContext *codec_ctx= NULL;AVFrame *frame = NULL;AVPacket *pkt = NULL;int ret = 0;int force_codec = 0;     // 强制使用指定的编码char *codec_name = NULL;if (argc < 3) {fprintf(stderr, "Usage: %s <input_file out_file [codec_name]>, argc:%d\n",argv[0], argc);return 0;}in_pcm_file = argv[1];      // 输入PCM文件out_aac_file = argv[2];     // 输出的AAC文件enum AVCodecID codec_id = AV_CODEC_ID_AAC;if(4 == argc) {if(strcmp(argv[3], "libfdk_aac") == 0) {force_codec = 1;     // 强制使用 libfdk_aaccodec_name = "libfdk_aac";} else if(strcmp(argv[3], "aac") == 0) {force_codec = 1;codec_name = "aac";}}if(force_codec)printf("force codec name: %s\n", codec_name);elseprintf("default codec name: %s\n", "aac");if(force_codec == 0) { // 没有强制设置编码器codec = avcodec_find_encoder(codec_id); // 按ID查找则缺省的aac encode为aacenc.c} else {// 按名字查找指定的encode,对应AVCodec的name字段codec = avcodec_find_encoder_by_name(codec_name);}if (!codec) {fprintf(stderr, "Codec not found\n");exit(1);}codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {fprintf(stderr, "Could not allocate audio codec context\n");exit(1);}codec_ctx->codec_id = codec_id;codec_ctx->codec_type = AVMEDIA_TYPE_AUDIO;codec_ctx->bit_rate = 128*1024;codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO;codec_ctx->sample_rate    = 48000; //48000;codec_ctx->channels       = av_get_channel_layout_nb_channels(codec_ctx->channel_layout);codec_ctx->profile = FF_PROFILE_AAC_LOW;    //if(strcmp(codec->name, "aac") == 0) {codec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;} else if(strcmp(codec->name, "libfdk_aac") == 0) {codec_ctx->sample_fmt = AV_SAMPLE_FMT_S16;} else {codec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;}/* 检测支持采样格式支持情况 */if (!check_sample_fmt(codec, codec_ctx->sample_fmt)) {fprintf(stderr, "Encoder does not support sample format %s",av_get_sample_fmt_name(codec_ctx->sample_fmt));exit(1);}if (!check_sample_rate(codec, codec_ctx->sample_rate)) {fprintf(stderr, "Encoder does not support sample rate %d", codec_ctx->sample_rate);exit(1);}if (!check_channel_layout(codec, codec_ctx->channel_layout)) {fprintf(stderr, "Encoder does not support channel layout %lu", codec_ctx->channel_layout);exit(1);}printf("\n\nAudio encode config\n");printf("bit_rate:%ldkbps\n", codec_ctx->bit_rate/1024);printf("sample_rate:%d\n", codec_ctx->sample_rate);printf("sample_fmt:%s\n", av_get_sample_fmt_name(codec_ctx->sample_fmt));printf("channels:%d\n", codec_ctx->channels);// frame_size是在avcodec_open2后进行关联printf("1 frame_size:%d\n", codec_ctx->frame_size);/* 将编码器上下文和编码器进行关联 */if (avcodec_open2(codec_ctx, codec, NULL) < 0) {fprintf(stderr, "Could not open codec\n");exit(1);}printf("2 frame_size:%d\n\n", codec_ctx->frame_size); // 决定每次到底送多少个采样点// 打开输入和输出文件infile = fopen(in_pcm_file, "rb");if (!infile) {fprintf(stderr, "Could not open %s\n", in_pcm_file);exit(1);}outfile = fopen(out_aac_file, "wb");if (!outfile) {fprintf(stderr, "Could not open %s\n", out_aac_file);exit(1);}/* packet for holding encoded output */pkt = av_packet_alloc();if (!pkt){fprintf(stderr, "could not allocate the packet\n");exit(1);}/* frame containing input raw audio */frame = av_frame_alloc();if (!frame){fprintf(stderr, "Could not allocate audio frame\n");exit(1);}/* 每次送多少数据给编码器由:*  (1)frame_size(每帧单个通道的采样点数);*  (2)sample_fmt(采样点格式);*  (3)channel_layout(通道布局情况);* 3要素决定*/frame->nb_samples     = codec_ctx->frame_size;frame->format         = codec_ctx->sample_fmt;frame->channel_layout = codec_ctx->channel_layout;frame->channels = av_get_channel_layout_nb_channels(frame->channel_layout);printf("frame nb_samples:%d\n", frame->nb_samples);printf("frame sample_fmt:%d\n", frame->format);printf("frame channel_layout:%lu\n\n", frame->channel_layout);/* 为frame分配buffer */ret = av_frame_get_buffer(frame, 0);if (ret < 0){fprintf(stderr, "Could not allocate audio data buffers\n");exit(1);}// 计算出每一帧的数据 单个采样点的字节 * 通道数目 * 每帧采样点数量int frame_bytes = av_get_bytes_per_sample(frame->format) \* frame->channels \* frame->nb_samples;printf("frame_bytes %d\n", frame_bytes);uint8_t *pcm_buf = (uint8_t *)malloc(frame_bytes);if(!pcm_buf) {printf("pcm_buf malloc failed\n");return 1;}uint8_t *pcm_temp_buf = (uint8_t *)malloc(frame_bytes);if(!pcm_temp_buf) {printf("pcm_temp_buf malloc failed\n");return 1;}int64_t pts = 0;printf("start enode\n");for (;;) {memset(pcm_buf, 0, frame_bytes);size_t read_bytes = fread(pcm_buf, 1, frame_bytes, infile);if(read_bytes <= 0) {printf("read file finish\n");break;
//            fseek(infile,0,SEEK_SET);
//            fflush(outfile);
//            continue;}/* 确保该frame可写, 如果编码器内部保持了内存参考计数,则需要重新拷贝一个备份目的是新写入的数据和编码器保存的数据不能产生冲突*/ret = av_frame_make_writable(frame);if(ret != 0)printf("av_frame_make_writable failed, ret = %d\n", ret);if(AV_SAMPLE_FMT_S16 == frame->format) {// 将读取到的PCM数据填充到frame去,但要注意格式的匹配, 是planar还是packed都要区分清楚ret = av_samples_fill_arrays(frame->data, frame->linesize,pcm_buf, frame->channels,frame->nb_samples, frame->format, 0);} else {// 将读取到的PCM数据填充到frame去,但要注意格式的匹配, 是planar还是packed都要区分清楚// 将本地的f32le packed模式的数据转为float palanarmemset(pcm_temp_buf, 0, frame_bytes);f32le_convert_to_fltp((float *)pcm_buf, (float *)pcm_temp_buf, frame->nb_samples);ret = av_samples_fill_arrays(frame->data, frame->linesize,pcm_temp_buf, frame->channels,frame->nb_samples, frame->format, 0);}// 设置ptspts += frame->nb_samples;frame->pts = pts;       // 使用采样率作为pts的单位,具体换算成秒 pts*1/采样率ret = encode(codec_ctx, frame, pkt, outfile);if(ret < 0) {printf("encode failed\n");break;}}/* 冲刷编码器 */encode(codec_ctx, NULL, pkt, outfile);// 关闭文件fclose(infile);fclose(outfile);// 释放内存if(pcm_buf) {free(pcm_buf);}if (pcm_temp_buf) {free(pcm_temp_buf);}av_frame_free(&frame);av_packet_free(&pkt);avcodec_free_context(&codec_ctx);printf("main finish, please enter Enter and exit\n");getchar();return 0;
}

12、视频编码代码

/**
* @projectName   08-02-encode_video
* @brief         视频编码,从本地读取YUV数据进行H264编码
* @author        Liao Qingfu
* @date          2020-04-16
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libavcodec/avcodec.h>
#include <libavutil/time.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>int64_t get_time()
{return av_gettime_relative() / 1000;  // 换算成毫秒
}
static int encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,FILE *outfile)
{int ret;/* send the frame to the encoder */if (frame)printf("Send frame %lld\n", frame->pts);/* 通过查阅代码,使用x264进行编码时,具体缓存帧是在x264源码进行,* 不会增加avframe对应buffer的reference*/ret = avcodec_send_frame(enc_ctx, frame);if (ret < 0){fprintf(stderr, "Error sending a frame for encoding\n");return -1;}while (ret >= 0){ret = avcodec_receive_packet(enc_ctx, pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return 0;} else if (ret < 0) {fprintf(stderr, "Error encoding audio frame\n");return -1;}if(pkt->flags & AV_PKT_FLAG_KEY)printf("Write packet flags:%d pts:%lld dts:%lld (size:%5d)\n",pkt->flags, pkt->pts, pkt->dts, pkt->size);if(!pkt->flags)printf("Write packet flags:%d pts:%lld dts:%lld (size:%5d)\n",pkt->flags, pkt->pts, pkt->dts, pkt->size);fwrite(pkt->data, 1, pkt->size, outfile);}return 0;
}
/*** @brief 提取测试文件:ffmpeg -i test_1280x720.flv -t 5 -r 25 -pix_fmt yuv420p yuv420p_1280x720.yuv*           参数输入: yuv420p_1280x720.yuv yuv420p_1280x720.h264 libx264* @param argc* @param argv* @return*/
int main(int argc, char **argv)
{char *in_yuv_file = NULL;char *out_h264_file = NULL;FILE *infile = NULL;FILE *outfile = NULL;const char *codec_name = NULL;const AVCodec *codec = NULL;AVCodecContext *codec_ctx= NULL;AVFrame *frame = NULL;AVPacket *pkt = NULL;int ret = 0;if (argc < 4) {fprintf(stderr, "Usage: %s <input_file out_file codec_name >, argc:%d\n",argv[0], argc);return 0;}in_yuv_file = argv[1];      // 输入YUV文件out_h264_file = argv[2];codec_name = argv[3];/* 查找指定的编码器 */codec = avcodec_find_encoder_by_name(codec_name);if (!codec) {fprintf(stderr, "Codec '%s' not found\n", codec_name);exit(1);}codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}/* 设置分辨率*/codec_ctx->width = 1280;codec_ctx->height = 720;/* 设置time base */codec_ctx->time_base = (AVRational)({1,25});codec_ctx->framerate = (AVRational)({25, 1});/* 设置I帧间隔* 如果frame->pict_type设置为AV_PICTURE_TYPE_I, 则忽略gop_size的设置,一直当做I帧进行编码*/codec_ctx->gop_size = 25;   // I帧间隔codec_ctx->max_b_frames = 2; // 如果不想包含B帧则设置为0codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;//if (codec->id == AV_CODEC_ID_H264) {// 相关的参数可以参考libx264.c的 AVOption options// ultrafast all encode time:2270ms// medium all encode time:5815ms// veryslow all encode time:19836msret = av_opt_set(codec_ctx->priv_data, "preset", "medium", 0);if(ret != 0) {printf("av_opt_set preset failed\n");}ret = av_opt_set(codec_ctx->priv_data, "profile", "main", 0); // 默认是highif(ret != 0) {printf("av_opt_set profile failed\n");}ret = av_opt_set(codec_ctx->priv_data, "tune","zerolatency",0); // 直播是才使用该设置
//        ret = av_opt_set(codec_ctx->priv_data, "tune","film",0); //  画质filmif(ret != 0) {printf("av_opt_set tune failed\n");}}/** 设置编码器参数*//* 设置bitrate */codec_ctx->bit_rate = 3000000;
//    codec_ctx->rc_max_rate = 3000000;
//    codec_ctx->rc_min_rate = 3000000;
//    codec_ctx->rc_buffer_size = 2000000;
//    codec_ctx->thread_count = 4;  // 开了多线程后也会导致帧输出延迟, 需要缓存thread_count帧后再编程。
//    codec_ctx->thread_type = FF_THREAD_FRAME; // 并 设置为FF_THREAD_FRAME/* 对于H264 AV_CODEC_FLAG_GLOBAL_HEADER  设置则只包含I帧,此时sps pps需要从codec_ctx->extradata读取*  不设置则每个I帧都带 sps pps sei*/
//    codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; // 存本地文件时不要去设置/* 将codec_ctx和codec进行绑定 */ret = avcodec_open2(codec_ctx, codec, NULL);if (ret < 0) {fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));exit(1);}printf("thread_count: %d, thread_type:%d\n", codec_ctx->thread_count, codec_ctx->thread_type);// 打开输入和输出文件infile = fopen(in_yuv_file, "rb");if (!infile) {fprintf(stderr, "Could not open %s\n", in_yuv_file);exit(1);}outfile = fopen(out_h264_file, "wb");if (!outfile) {fprintf(stderr, "Could not open %s\n", out_h264_file);exit(1);}// 分配pkt和framepkt = av_packet_alloc();if (!pkt) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}// 为frame分配bufferframe->format = codec_ctx->pix_fmt;frame->width  = codec_ctx->width;frame->height = codec_ctx->height;ret = av_frame_get_buffer(frame, 0);if (ret < 0) {fprintf(stderr, "Could not allocate the video frame data\n");exit(1);}// 计算出每一帧的数据 像素格式 * 宽 * 高// 1382400int frame_bytes = av_image_get_buffer_size(frame->format, frame->width,frame->height, 1);printf("frame_bytes %d\n", frame_bytes);uint8_t *yuv_buf = (uint8_t *)malloc(frame_bytes);if(!yuv_buf) {printf("yuv_buf malloc failed\n");return 1;}int64_t begin_time = get_time();int64_t end_time = begin_time;int64_t all_begin_time = get_time();int64_t all_end_time = all_begin_time;int64_t pts = 0;printf("start enode\n");for (;;) {memset(yuv_buf, 0, frame_bytes);size_t read_bytes = fread(yuv_buf, 1, frame_bytes, infile);if(read_bytes <= 0) {printf("read file finish\n");break;}/* 确保该frame可写, 如果编码器内部保持了内存参考计数,则需要重新拷贝一个备份目的是新写入的数据和编码器保存的数据不能产生冲突*/int frame_is_writable = 1;if(av_frame_is_writable(frame) == 0) { // 这里只是用来测试printf("the frame can't write, buf:%p\n", frame->buf[0]);if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针printf("ref_count1(frame) = %d\n", av_buffer_get_ref_count(frame->buf[0]));frame_is_writable = 0;}ret = av_frame_make_writable(frame);if(frame_is_writable == 0) {  // 这里只是用来测试printf("av_frame_make_writable, buf:%p\n", frame->buf[0]);if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针printf("ref_count2(frame) = %d\n", av_buffer_get_ref_count(frame->buf[0]));}if(ret != 0) {printf("av_frame_make_writable failed, ret = %d\n", ret);break;}int need_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf,frame->format,frame->width, frame->height, 1);if(need_size != frame_bytes) {printf("av_image_fill_arrays failed, need_size:%d, frame_bytes:%d\n",need_size, frame_bytes);break;}pts += 40;// 设置ptsframe->pts = pts;       // 使用采样率作为pts的单位,具体换算成秒 pts*1/采样率begin_time = get_time();ret = encode(codec_ctx, frame, pkt, outfile);end_time = get_time();printf("encode time:%lldms\n", end_time - begin_time);if(ret < 0) {printf("encode failed\n");break;}}/* 冲刷编码器 */encode(codec_ctx, NULL, pkt, outfile);all_end_time = get_time();printf("all encode time:%lldms\n", all_end_time - all_begin_time);// 关闭文件fclose(infile);fclose(outfile);// 释放内存if(yuv_buf) {free(yuv_buf);}av_frame_free(&frame);av_packet_free(&pkt);avcodec_free_context(&codec_ctx);printf("main finish, please enter Enter and exit\n");getchar();return 0;
}

http://www.ppmy.cn/news/1389707.html

相关文章

如何实现在固定位置的鼠标连点

鼠大侠的鼠标连点功能是免费的 浏览器搜索下载鼠大侠&#xff0c;指定连点间隔和启动快捷键 点击设置&#xff0c;指定点击位置

Python快速导入id至json文件(2024.3.19)

Python实现id导入至json文件 2024.3.19 需求分析1、输入数据介绍1.1 三个.txt文件1.1.1 computers.txt&#xff08;计算机&#xff09;1.1.2 cameras.txt&#xff08;摄像头&#xff09;1.1.3 monitors.txt&#xff08;显示器&#xff09; 1.2 单个.xlsx文件 2、实现思路3、Pyt…

docker设置容器独立ip(linux下虚拟机设置独立ip)

docker设置容器独立ip&#xff08;linux下虚拟机设置独立ip&#xff09; 在linux要设置容器或者其他虚拟机独立ip&#xff0c;需要如下步骤&#xff1a; 准备好ip和网关创建好网桥&#xff0c;并把物理网卡连接到网桥上对于docker容器来说&#xff0c;需要使用pipework配置容…

实验7-1-11 装箱问题(PTA)

题目&#xff1a; 假设有N项物品&#xff0c;大小分别为s1​、s2​、…、si​、…、sN​&#xff0c;其中si​为满足1≤si​≤100的整数。要把这些物品装入到容量为100的一批箱子&#xff08;序号1-N&#xff09;中。装箱方法是&#xff1a;对每项物品, 顺序扫描箱子&#xff…

【小白刷leetcode】第15题

【小白刷leetcode】第15题 动手刷leetcode&#xff0c;正在准备蓝桥&#xff0c;但是本人算法能力一直是硬伤。。。所以做得一直很痛苦。但是不熟练的事情像练吉他一样&#xff0c;就需要慢速&#xff0c;多练。 题目描述 看这个题目&#xff0c;说实在看的不是很懂。索性我们直…

为什么要进行无功补偿?无功补偿的原理、形式是什么?

彭姝麟 Acrelpsl 提及“无功补偿”&#xff0c;首先我们要了解无功功率的概念&#xff0c;什么是无功功率&#xff1f; 人们对有功功率相对容易理解&#xff0c;因为它能做功、产生热量、带动电机旋转等。例如&#xff0c;当交流电通过纯电阻时&#xff0c;电流能使电阻发热&a…

获取比特币和莱特币的实时价格

数据来源&#xff1a; https://datacenter.jin10.com/reportType/dc_bitcoin_current 代码&#xff1a; import akshare as ak import pandas as pd pd.set_option(display.max_columns, None) pd.set_option(display.max_rows, None) pd.set_option(display.width, 1000)cr…

3703. 括号的匹配 北京师范大学考研上机真题 栈的思想

在算术表达式中&#xff0c;除了加、减、乘、除等运算外&#xff0c;往往还有括号。 包括有大括号 {}&#xff0c;中括号 []&#xff0c;小括号 ()&#xff0c;尖括号 <> 等。 对于每一对括号&#xff0c;必须先左边括号&#xff0c;然后右边括号&#xff1b;如果有多个…