2

使用 ffmpeg 进行音频/视频编码:

我正在尝试使用 ffmpeg 创建一个带有编码视频和音频的 avi 文件。

首先,我创建文件:

            //define BITRATE 10000000
            //define GOP 300
            //define FPS 60
            //define VIDEOTYPE "avi"

            if (!encoder_->createFile(QFileInfo(*(videoFile_.data())).absoluteFilePath(), targetRect.width(), targetRect.height(), BITRATE*(1000 / FPS), GOP, 1000))    

缓冲区初始化为:

            audio_outbuf_size = 44100 * 0.005 * 16; //5ms of audio should be encoded, each time this function is called
            audio_outbuf = new uint8_t[audio_outbuf_size];

            outbuf_size = getWidth()*getHeight() * 3;        
            outbuf = new uint8_t[outbuf_size];

然后添加音频和视频流(音频:CODEC_ID_PCM_S16LE,16000 kb/s 和 44100 Hz,视频:PIX_FMT_YUV420P)

            void MediaMuxer::addAudioStream(QString fileName, ffmpeg::CodecID codec_id)
            {
                // Add the audio stream
                ffmpeg::AVCodec *encoder = avcodec_find_encoder(codec_id);
                pAudioStream_ = ffmpeg::av_new_stream(pOutputFormatCtx_, 0);
                if (!pAudioStream_) {
                    printf("Could not allocate stream\n");
                    return;
                }

                pAudioCodecCtx_ = pAudioStream_->codec;
                pAudioCodecCtx_->codec_id = codec_id;
                pAudioCodecCtx_->codec_type = ffmpeg::AVMEDIA_TYPE_AUDIO;
                pAudioCodecCtx_->sample_fmt = ffmpeg::AV_SAMPLE_FMT_S16;
                pAudioCodecCtx_->sample_fmt = encoder->sample_fmts[0];
                pAudioCodecCtx_->bit_rate = 16000;
                //pAudioCodecCtx_->bit_rate = 64000;
                pAudioCodecCtx_->sample_rate = N;
                pAudioCodecCtx_->channels = 1;

                pAudioCodecCtx_->time_base.den = FPS;
                pAudioCodecCtx_->time_base.num = 1;

                avcodec_thread_init(pAudioCodecCtx_, 10);

                // some formats want stream headers to be separate
                if (pOutputFormatCtx_->oformat->flags & AVFMT_GLOBALHEADER)
                    pAudioCodecCtx_->flags |= CODEC_FLAG_GLOBAL_HEADER;

                if (av_set_parameters(pOutputFormatCtx_, NULL) < 0)
                {
                    printf("Invalid output format parameters\n");
                    return;
                }

                //ffmpeg::dump_format(pOutputFormatCtx_, 0, fileName.toStdString().c_str(), 1);

                // open_video
                // find the audio encoder
                pAudioCodec_ = avcodec_find_encoder(pAudioCodecCtx_->codec_id);
                if (!pAudioCodec_)
                {
                    printf("codec not found\n");
                    return;
                }
                // open the codec
                if (avcodec_open(pAudioCodecCtx_, pAudioCodec_) < 0)
                {
                    printf("could not open codec\n");
                    return;
                }

                // Allocate memory for output
                if (!initAudioOutputBuf())
                {
                    printf("Can't allocate memory for audio output bitstream\n");
                    return;
                }

                // Allocate the audio frame
                if (!initAudioFrame())
                {
                    printf("Can't init audio frame\n");
                    return;
                }

                if (url_fopen(&pOutputFormatCtx_->pb, fileName.toStdString().c_str(), URL_WRONLY) < 0)
                {
                    printf("Could not open '%s'\n", fileName.toStdString().c_str());
                    return;
                }
                av_write_header(pOutputFormatCtx_);
            }

            void MediaMuxer::addVideoStream(QString fileName)
            {
                // Add the video stream
                pVideoStream_ = ffmpeg::av_new_stream(pOutputFormatCtx_, 0);
                if (!pVideoStream_)
                {
                    printf("Could not allocate stream\n");
                    return;
                }

                pVideoCodecCtx_ = pVideoStream_->codec;
                pVideoCodecCtx_->codec_id = pOutputFormat_->video_codec;
                pVideoCodecCtx_->codec_type = ffmpeg::AVMEDIA_TYPE_VIDEO;

                pVideoCodecCtx_->bit_rate = Bitrate;
                pVideoCodecCtx_->width = getWidth();
                pVideoCodecCtx_->height = getHeight();
                pVideoCodecCtx_->time_base.den = FPS;
                pVideoCodecCtx_->time_base.num = 1;
                pVideoCodecCtx_->gop_size = Gop;
                pVideoCodecCtx_->pix_fmt = ffmpeg::PIX_FMT_YUV420P;

                avcodec_thread_init(pVideoCodecCtx_, 10);

                // some formats want stream headers to be separate
                if (pOutputFormatCtx_->oformat->flags & AVFMT_GLOBALHEADER)
                    pVideoCodecCtx_->flags |= CODEC_FLAG_GLOBAL_HEADER;


                if (av_set_parameters(pOutputFormatCtx_, NULL) < 0)
                {
                    printf("Invalid output format parameters\n");
                    return;
                }

                //ffmpeg::dump_format(pOutputFormatCtx_, 0, fileName.toStdString().c_str(), 1);

                // open_video
                // find the video encoder
                pVideoCodec_ = avcodec_find_encoder(pVideoCodecCtx_->codec_id);
                if (!pVideoCodec_)
                {
                    printf("codec not found\n");
                    return;
                }
                // open the codec
                if (avcodec_open(pVideoCodecCtx_, pVideoCodec_) < 0)
                {
                    printf("could not open codec\n");
                    return;
                }

                // Allocate memory for output
                if (!initOutputBuf())
                {
                    printf("Can't allocate memory for output bitstream\n");
                    return;
                }

                // Allocate the YUV frame
                if (!initFrame())
                {
                    printf("Can't init frame\n");
                    return;
                }

                if (url_fopen(&pOutputFormatCtx_->pb, fileName.toStdString().c_str(), URL_WRONLY) < 0)
                {
                    printf("Could not open '%s'\n", fileName.toStdString().c_str());
                    return;
                }
                av_write_header(pOutputFormatCtx_);
            }

最后,我交替调用 encodeVideo/encodeAudio 以在特定录制时间(pts)对视频和 PCM 音频帧进行编码:

            int MediaMuxer::encodeVideo(const QImage &img, unsigned pts)
            {
                convertImage_sws(img);     // SWS conversion
                pVideoCodecCtx_->coded_frame->pts = pts;  // Set the time stamp
                int out_size = ffmpeg::avcodec_encode_video(pVideoCodecCtx_, outbuf, outbuf_size, ppicture);        
                pVideoCodecCtx_->coded_frame->pts = pts;  // Set the time stamp

                if (out_size > 0)
                {
                    ffmpeg::av_init_packet(&pkt);       
                    if (pVideoCodecCtx_->coded_frame->pts != (0x8000000000000000LL))
                        pkt.pts = av_rescale_q(pVideoCodecCtx_->coded_frame->pts, pVideoCodecCtx_->time_base, pVideoStream_->time_base);
                    if (pVideoCodecCtx_->coded_frame->key_frame)
                        pkt.flags |= AV_PKT_FLAG_KEY;

                    pkt.stream_index = pVideoStream_->index;
                    pkt.data = outbuf;
                    pkt.size = out_size;
                    int ret = ffmpeg::av_interleaved_write_frame(pOutputFormatCtx_, &pkt);      
                    if (ret<0)
                        return -1;

                }
                return out_size;
            }

            int MediaMuxer::encodeAudio(unsigned pts)
            {
                pAudioCodecCtx_->coded_frame->pts = pts;  // Set the time stamp

                // simple sound encoding    
                int16_t samples[220] = { 0 }; // buffer
                int n;                // buffer index
                double Fs = 44100.0;  // sampling frequency

                // Generate audio data
                for (n = 0; n < 220; ++n)   //220 samples (44100*.005sec as the interval between 2 video frames is 10ms)
                    samples[n] = 16383.0 * sin(n*1000.0*2.0*M_PI / Fs); //sine wav

                int  out_size = ffmpeg::avcodec_encode_audio(pAudioCodecCtx_, audio_outbuf, audio_outbuf_size, (const short*)samples);

                pAudioCodecCtx_->coded_frame->pts = pts;  // Set the time stamp

                if (out_size>0)
                {
                    // Packet
                    ffmpeg::AVPacket pkt = { 0 };
                    av_init_packet(&pkt);
                    pkt.data = NULL; // packet data will be allocated by the encoder
                    pkt.size = 0;
                    if (pAudioCodecCtx_->coded_frame->pts != (0x8000000000000000LL))
                        pkt.pts = av_rescale_q(pAudioCodecCtx_->coded_frame->pts, pAudioCodecCtx_->time_base, pAudioStream_->time_base);
                    if (pAudioCodecCtx_->coded_frame->key_frame)
                        pkt.flags |= AV_PKT_FLAG_KEY;

                    pkt.stream_index = pAudioStream_->index;
                    pkt.data = audio_outbuf;

                    pkt.size = out_size;
                    int ret = av_interleaved_write_frame(pOutputFormatCtx_, &pkt);
                    if (ret<0)
                        return -1;
                    av_free_packet(&pkt);
                }       
                //end simple sound encoding

                return pkt.size;
            }

结果是一个不错的视频,后面有一些音频(或者是定期发出的哔哔声,但比视频更早结束,或者是持续时间比视频短的连续较长的声音)。

每次调用函数 encodeAudio() 时,我想以不规则的间隔生成哔声。我尝试修改采样率、缓冲区大小、pkt 大小和样本数,但没有任何成功。我也尝试在不同的时间设置积分,但它并没有让我到达我想去的地方。有人可以帮忙吗?

4

0 回答 0