其他分享
首页 > 其他分享> > ffmpeg转码transcode,不依赖filter

ffmpeg转码transcode,不依赖filter

作者:互联网

此代码可以把视频编码方式为H264,音频编码方式为AAC的FLV转换成视频编码方式为mpg1video,音频编码方式为AAC的MP4。此程序资源释放可能有问题

包括了视频的解复用,复用,转码,时间戳转换

ffmpeg版本4.3

static int nVideoStream_idx_in = -1;//输入文件的视频流编号
static int nAudioStream_idx_in = -1;//输入文件的音频流编号

static void openVidoEncoder_mpg1video(int width, int height,AVFormatContext *ofmt_ctx,AVCodecContext** enc_ctx)
{
    int nRet = 0;
    AVStream *stream = nullptr;
    AVCodec *codec_mpeg1 = avcodec_find_encoder(AV_CODEC_ID_MPEG1VIDEO);
    if(!codec_mpeg1)
    {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    if (!(stream = avformat_new_stream(ofmt_ctx, codec_mpeg1)))
    {
        fprintf(stderr, "Could not allocate video codec context\n");
        return;
    }
    (*enc_ctx) = avcodec_alloc_context3(codec_mpeg1);
    if ((* enc_ctx) == nullptr)
    {
        fprintf(stderr, "Could not allocate video codec context\n");
        return;
    }
    /* put sample parameters */
    (*enc_ctx)->bit_rate = 100000;
    /* resolution must be a multiple of two */
    (*enc_ctx)->width = width;
    (*enc_ctx)->height = height;
    /* frames per second */
    (*enc_ctx)->time_base = (AVRational){1,25};
    (*enc_ctx)->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
     */
    (*enc_ctx)->pix_fmt = AV_PIX_FMT_YUV420P;
    (*enc_ctx)->codec_id = codec_mpeg1->id;
    (*enc_ctx)->codec_type = AVMEDIA_TYPE_VIDEO;
    (*enc_ctx)->gop_size = 20;
    if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
        ofmt_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

    if(avcodec_open2((*enc_ctx),codec_mpeg1,nullptr) < 0)
    {
        printf("avcodec_open2 fail.\n");
    }
    nRet = avcodec_parameters_from_context(stream->codecpar,(*enc_ctx));
    if(nRet < 0)
    {
        printf("avcodec_parameters_to_context fail.");
        return ;
    }
    return;
}

static void openAudioEncoder( AVCodecContext** enc_ctx,
                                  AVFormatContext **ofmt_ctx,
                                  AVStream *in_stream_a)
{
    int nRet = 0;
    AVStream *stream = nullptr;
    //音频不做转码,使用和输入音频流一样的解码器
    AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_AAC);
    if(!codec)
    {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    if (!(stream = avformat_new_stream(*ofmt_ctx, codec)))
    {
        fprintf(stderr, "Could not allocate video codec context\n");
        return;
    }

    (*enc_ctx) = avcodec_alloc_context3(codec);
    if ((* enc_ctx) == nullptr)
    {
        fprintf(stderr, "Could not allocate video codec context\n");
        return;
    }
    //Copy the settings of AVCodecContext
    if (avcodec_parameters_to_context((* enc_ctx),in_stream_a->codecpar)< 0) {
        printf( "Failed to copy context from input to output stream codec context\n");
        return ;
    }
    if(avcodec_open2((*enc_ctx),codec,nullptr) < 0)
    {
        printf("avcodec_open2 fail.\n");
    }
    stream->codecpar->codec_tag = 0;
    if ((*ofmt_ctx)->oformat->flags & AVFMT_GLOBALHEADER)
    {
        (*enc_ctx)->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }
    //将AVCodecContext的成员复制到AVCodecParameters结构体。前后两行不能调换顺序
    avcodec_parameters_from_context(stream->codecpar, (* enc_ctx));
    return;
}
static void open_input(const char* pInFileName,
                       AVFormatContext **pInFmtCtx,
                       AVCodecContext** pInCodecCtx_v,
                       AVCodecContext** pInCodecCtx_a)
{
    int nRet = 0;
    AVDictionary *pDic = nullptr;
    nRet = avformat_open_input(pInFmtCtx,pInFileName,nullptr,&pDic);
    if( nRet < 0)
    {
        printf("Could not open input file.");
        return;
    }
    avformat_find_stream_info(*pInFmtCtx, nullptr);
    printf("===========Input Information==========\n");
    av_dump_format(*pInFmtCtx, 0, pInFileName, 0);
    printf("======================================\n");
    for (int i = 0; i < (*pInFmtCtx)->nb_streams; i++)
    {
        if((*pInFmtCtx)->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            nVideoStream_idx_in = i;
            AVStream * in_stream_v = (*pInFmtCtx)->streams[i];
            AVCodec *pInCodec_v = avcodec_find_decoder(in_stream_v->codecpar->codec_id);
            if(nullptr == pInCodec_v)
            {
                printf("inpout video avcodec_find_decoder fail.");
                return;
            }
            *pInCodecCtx_v = avcodec_alloc_context3(pInCodec_v);
            nRet = avcodec_parameters_to_context(*pInCodecCtx_v, in_stream_v->codecpar);
            if(nRet < 0)
            {
                printf("avcodec_parameters_to_context fail.");
                return;
            }
            //打开视频解码器
            if(avcodec_open2(*pInCodecCtx_v, pInCodec_v, nullptr) < 0)
            {
                printf("Error: Can't open codec!\n");
                return ;
            }
            if(nRet < 0)
            {
                printf("avcodec_parameters_from_context fail.");
                return;
            }
            printf("width = %d\n", (*pInCodecCtx_v)->width);
            printf("height = %d\n",(*pInCodecCtx_v)->height);
            if(nRet < 0)
            {
                printf("inpout video avcodec_parameters_to_context fail.");
                return;
            }
        }
        if((*pInFmtCtx)->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            nAudioStream_idx_in = i;
            AVStream * in_stream_a = (*pInFmtCtx)->streams[i];
            AVCodec *pInCodec_a = avcodec_find_decoder(in_stream_a->codecpar->codec_id);
            if(nullptr == pInCodec_a)
            {
                printf("inpout audio avcodec_find_decoder fail.\n");
                return;
            }
            *pInCodecCtx_a = avcodec_alloc_context3(pInCodec_a);

            nRet = avcodec_parameters_to_context(*pInCodecCtx_a, in_stream_a->codecpar);
            if(nRet < 0)
            {
                printf("avcodec_parameters_to_context fail.\n");
                return;
            }
            if(nRet < 0)
            {
                printf("avcodec_parameters_from_context fail.\n");
                return;
            }
            //打开音频解码器
            if(avcodec_open2(*pInCodecCtx_a, pInCodec_a, nullptr) < 0)
            {
                printf("Error: Can't open codec!\n");
                return ;
            }
        }
    }
}

static void open_output(const char* pOutFileName,
                        AVCodecContext* pInCodecCtx_v,
                        AVCodecContext* pInCodecCtx_a,
                        AVFormatContext** ofmt_ctx,
                        AVCodecContext** encVideoCtx,
                        AVCodecContext** encAudioctx,
                        AVStream *in_stream_a)
{
    AVOutputFormat *ofmt = nullptr;
    //Output
    avformat_alloc_output_context2(ofmt_ctx, nullptr, nullptr, pOutFileName);
    if (!ofmt_ctx) {
        printf( "Could not create output context\n");
        return ;
    }
    openVidoEncoder_mpg1video(pInCodecCtx_v->width,pInCodecCtx_v->height,*ofmt_ctx,encVideoCtx);
    openAudioEncoder(encAudioctx,ofmt_ctx,in_stream_a);

    ofmt = (*ofmt_ctx)->oformat;
    //Open output file
    if (!(ofmt->flags & AVFMT_NOFILE))
    {
        if (avio_open(&(*ofmt_ctx)->pb, pOutFileName, AVIO_FLAG_WRITE) < 0)
        {
            printf( "Could not open output file '%s'", pOutFileName);
            return ;
        }
    }
    printf("==========Output Information==========\n");
    av_dump_format(*ofmt_ctx, 0, pOutFileName, 1);
    printf("======================================\n");
}

static void test()
{
    int nRet = 0;
    const char *pInFileName = "D:/videos/new_killer.flv";
    const char *pOutFileName = "D:/videos/_new_killer.mp4"; 
    AVFormatContext *pInFmtCtx = nullptr;
    AVFormatContext *ofmt_ctx = nullptr;
    AVCodecContext* encVideoCtx = nullptr;
    AVCodecContext* encAudioctx = nullptr;
    AVCodecContext* pInCodecCtx_v = nullptr;
    AVCodecContext* pInCodecCtx_a = nullptr;

    int got_picture = 0;
    AVPacket pkt;
    AVPacket newpkt;
    av_init_packet(&pkt);
    av_init_packet(&newpkt);
    AVFrame *pFrame = av_frame_alloc();

    open_input(pInFileName,
               &pInFmtCtx,
               &pInCodecCtx_v,
               &pInCodecCtx_a);

    AVStream * in_stream_a = pInFmtCtx->streams[nAudioStream_idx_in];
    open_output(pOutFileName,
                pInCodecCtx_v,
                pInCodecCtx_a,
                &ofmt_ctx,
                &encVideoCtx,
                &encAudioctx,
                in_stream_a);

    //Write file header
    nRet = avformat_write_header(ofmt_ctx, nullptr);
    if ( nRet < 0)
    {
        printf( "Error occurred when opening output file\n");
        return ;
    }
    while (1)
    {
        AVStream *in_stream = nullptr;
        AVStream *out_stream= nullptr;
        //读取一帧到pkt,视频是1帧,音频可能1帧或者多帧
        nRet = av_read_frame(pInFmtCtx, &pkt);
        if( nRet < 0)
        {
            printf( "av_read_frame end \n");
            break ;
        }
        if(pkt.stream_index == nVideoStream_idx_in)
        {
        	//把pkt发送给解码器pInCodecCtx_v解码(后台进行)
            nRet = avcodec_send_packet(pInCodecCtx_v, &pkt);
            if(nRet <0 )
            {
                av_packet_unref(&pkt);
                std::cout<<"avcodec_send_packet fail \n";
                break;
            }
            //从pInCodecCtx_v获取解码好的原始数据到pFrame
            got_picture =avcodec_receive_frame(pInCodecCtx_v, pFrame);
            if(0 == got_picture)//转码
            {
                //送pFrame原始数据给编码器进行编码
                nRet = avcodec_send_frame(encVideoCtx,pFrame);
                if(nRet < 0)
                {
                    printf("avcodec_send_frame fail.\n");
                    return;
                }
                //从编码器获取编号的数据
                while(nRet >= 0)
                {
                	//编码好的数据发送到newpkt
                    nRet = avcodec_receive_packet(encVideoCtx,&newpkt);
                    //返回值为-11意味着需要新的输入数据才能返回新的输出。
                    //在解码或编码开始时,编解码器可能会接收多个输入帧/数据包而不返回帧,直到其内部缓冲区被填充为止。
                    if( AVERROR(EAGAIN) == nRet || AVERROR_EOF == nRet)
                    {
                        av_packet_unref(&newpkt);
                        break;
                    }
                    else if(nRet < 0 )
                    {
                        printf("avcodec_receive_packet fail.\n");
                        return;
                    }
                    //转换PTS/DTS(Convert PTS/DTS)
                    in_stream  = pInFmtCtx->streams[pkt.stream_index];
                    out_stream = ofmt_ctx->streams[pkt.stream_index];
                    newpkt.pts = av_rescale_q_rnd(newpkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
                    newpkt.dts = av_rescale_q_rnd(newpkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
                    newpkt.duration = av_rescale_q(newpkt.duration, in_stream->time_base, out_stream->time_base);
                    newpkt.pos = -1;
                    printf("video Write 1 Packet. size:%5d\tpts:%lld\n",pkt.size,pkt.pts);
                    //写入(Write)
                    nRet = av_interleaved_write_frame(ofmt_ctx, &newpkt);
                    if (nRet < 0)
                    {
                        printf( "Error muxing packet\n");
                        break;
                    }
                    av_packet_unref(&newpkt);
                }
            }
        }
        else if(pkt.stream_index == nAudioStream_idx_in)
        {
            //转换PTS/DTS(Convert PTS/DTS)
            in_stream  = pInFmtCtx->streams[pkt.stream_index];
            out_stream = ofmt_ctx->streams[pkt.stream_index];
            /* copy packet */
            //转换PTS/DTS(Convert PTS/DTS)
            AVRational temp1 = in_stream->time_base;
            AVRational temp2 = out_stream->time_base;
            pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
            pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
            pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
            pkt.pos = -1;
            printf("audio Write 1 Packet. size:%5d\tpts:%lld\n",pkt.size,pkt.pts);
            //写入(Write)
            nRet = av_interleaved_write_frame(ofmt_ctx, &pkt);
            if (nRet < 0)
            {
                printf( "Error muxing packet\n");
                break;
            }
        }
        av_packet_unref(&pkt);
    }
    //Write file trailer
    av_write_trailer(ofmt_ctx);
    printf( "av_write_trailer \n");
    printf( "==================================================== \n");
    av_dump_format(ofmt_ctx, 0, pOutFileName, 1);
    printf( "==================================================== \n");
    avformat_close_input(&pInFmtCtx);
    avio_close(ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);
}
int main()
{
    test();
    std::cout<<"end";
}

标签:ffmpeg,stream,avcodec,nRet,ctx,filter,transcode,printf,ofmt
来源: https://blog.csdn.net/asdasfdgdhh/article/details/111316934