欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

音视频 ffmpeg库使用

程序员文章站 2022-03-04 23:41:59
...

ffmpeg 4.2.4 

 源代码

extern "C"
{
    #include "libavformat/avformat.h"
    #include "libavcodec/avcodec.h"
    #include "libswscale/swscale.h"
    #include "libswresample/swresample.h"
    #include "libavfilter/avfilter.h"
    #include "libavfilter/buffersink.h"
    #include "libavfilter/buffersrc.h"
    #include "libavutil/error.h"
    #include "libavutil/rational.h"
}


#include <iostream>
#include <fstream>
#include <cassert>
#include <cstring>
#include <csignal>


bool g_bStop = false;

void on_ctrl_c(int ) 
{
    g_bStop = true;
}

void list_demuxer()
{
    void* opaque = nullptr;
    const AVInputFormat* pIFormat = av_demuxer_iterate(&opaque);
    while (pIFormat != nullptr)
    {
        std::cout << "" << pIFormat->name << std::endl;
        pIFormat = av_demuxer_iterate(&opaque);
    }
}

void read_file(const char* szFilename)
{
    do 
    {
        std::fstream fs;
        fs.open(szFilename, std::fstream::in | std::fstream::binary);
        if (!fs.is_open())
        {
            std::cout << "open file failed" << std::endl;
            break;
        }
        char buffer[1024] = "";
        for (int i = 0;; i++)
        {
            fs.read(buffer, 1024);
            auto iReadLength = fs.gcount();
            if(fs.eof()) break;
        }
        fs.close();
    } while (false);

}

// Entry
int main(int argc, char** argv)
{
    int iRet = 0;

    std::cout << "Start ." << std::endl;
    signal(SIGINT, &on_ctrl_c);

#if 0
    list_demuxer();
    read_file("normal.h264");
#endif
    
    std::string szMediaFilename = argc > 1 ? argv[1] : "normal.mp4";
    std::string szPrefixMediaFilename = szMediaFilename.substr(0, szMediaFilename.rfind('.'));
    char szErrorBuffer[AV_ERROR_MAX_STRING_SIZE] = "";
    AVFormatContext* pMediaFormatCtx = nullptr;
    
    do
    {
        // AVFormatContext
        iRet = avformat_open_input(&pMediaFormatCtx, szMediaFilename.c_str(), 0, 0);
        if(iRet != 0) 
        {
            av_strerror(iRet, szErrorBuffer, AV_ERROR_MAX_STRING_SIZE);
            std::cout << "avformat_open_input failed " << szErrorBuffer << std::endl;
            break;
        }
        iRet = avformat_find_stream_info(pMediaFormatCtx, 0);
        if (iRet != 0)
        {
            av_strerror(iRet, szErrorBuffer, AV_ERROR_MAX_STRING_SIZE);
            std::cout << "avformat_find_stream_info failed " << szErrorBuffer << std::endl;
            break;
        }
        // AVStream
        AVStream* pVideoStream = nullptr,* pAudioStream = nullptr;
        for (unsigned int i = 0; i < pMediaFormatCtx->nb_streams; i++)
        {
            if (pMediaFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                pVideoStream = pMediaFormatCtx->streams[i];
            }
            if (pMediaFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
            {
                pAudioStream = pMediaFormatCtx->streams[i];
            }
        }
        if (pVideoStream == nullptr)
        {
            std::cout << "pVideoStream empty " << std::endl;
            break;
        }
        if (pAudioStream == nullptr)
        {
            std::cout << "pAudioStream empty " << std::endl;
            break;
        }
        // AVCodecContext
        AVCodec* pVideoCodec = avcodec_find_decoder(pVideoStream->codecpar->codec_id);
        if (pVideoCodec == nullptr)
        {
            std::cout << "avcodec_find_decoder failed " << std::endl;
            break;
        }
        AVCodecContext* pVideoCodecCtx = avcodec_alloc_context3(pVideoCodec);
        if (pVideoCodecCtx == nullptr)
        {
            std::cout << "avcodec_alloc_context3 failed " << std::endl;
            break;
        }
        iRet = avcodec_parameters_to_context(pVideoCodecCtx, pVideoStream->codecpar);
        if (iRet < 0)
        {
            av_strerror(iRet, szErrorBuffer, AV_ERROR_MAX_STRING_SIZE);
            std::cout << "avcodec_parameters_to_context failed " << szErrorBuffer << std::endl;
            break;
        }
        AVCodec* pAudioCodec = avcodec_find_decoder(pAudioStream->codecpar->codec_id);
        if (pAudioCodec == nullptr)
        {
            std::cout << "avcodec_find_decoder failed " << std::endl;
            break;
        }
        AVCodecContext* pAudioCodecCtx = avcodec_alloc_context3(pAudioCodec);
        if (pAudioCodecCtx == nullptr)
        {
            std::cout << "avcodec_alloc_context3 failed " << std::endl;
            break;
        }
        iRet = avcodec_parameters_to_context(pAudioCodecCtx, pAudioStream->codecpar);
        if (iRet < 0)
        {
            av_strerror(iRet, szErrorBuffer, AV_ERROR_MAX_STRING_SIZE);
            std::cout << "avcodec_parameters_to_context failed " << szErrorBuffer << std::endl;
            break;
        }
        // open decoder
        iRet = avcodec_open2(pVideoCodecCtx, pVideoCodec, nullptr);
        if(iRet != 0)
        {
            av_strerror(iRet, szErrorBuffer, AV_ERROR_MAX_STRING_SIZE);
            std::cout << "avcodec_open2 failed " << szErrorBuffer << std::endl;
        }
        iRet = avcodec_open2(pAudioCodecCtx, pAudioCodec, nullptr);
        if (iRet != 0)
        {
            av_strerror(iRet, szErrorBuffer, AV_ERROR_MAX_STRING_SIZE);
            std::cout << "avcodec_open2 failed " << szErrorBuffer << std::endl;
        }
        // decode 
        AVPacket sMediapacket;
        AVFrame* pMediaFrame = av_frame_alloc();
        std::fstream videostream, audiostream;
        std::fstream videobigstream, audiobigstream;

        do
        {
            iRet = av_read_frame(pMediaFormatCtx, &sMediapacket);
            if (iRet < 0)
            {
                av_strerror(iRet, szErrorBuffer, AV_ERROR_MAX_STRING_SIZE);
                std::cout << "av_read_frame failed " << szErrorBuffer << std::endl;
                break;
            }
            if (sMediapacket.pts < 0)
            {
                std::cout << "avpacket pts: " << sMediapacket.pts << std::endl;
                continue;
            }
            // video decode
            if (sMediapacket.stream_index == pVideoStream->index)
            {
                iRet = avcodec_send_packet(pVideoCodecCtx, &sMediapacket);
                if (iRet != 0)
                {
                    av_strerror(iRet, szErrorBuffer, AV_ERROR_MAX_STRING_SIZE);
                    std::cout << "warning avcodec_send_packet failed " << szErrorBuffer << std::endl;
                    break;
                }
                iRet = avcodec_receive_frame(pVideoCodecCtx, pMediaFrame);
                if (iRet != 0)
                {
                    av_strerror(iRet, szErrorBuffer, AV_ERROR_MAX_STRING_SIZE);
                    std::cout << "warning avcodec_receive_frame failed " << szErrorBuffer << std::endl;
                    break;
                }
                // write h264 stream
                if(pVideoCodecCtx->codec_id == AV_CODEC_ID_H264)
                {
                    char szStart3Code[3] = { 0x00,0x00, 0x01 };
                    char szStart4Code[4] = { 0x00,0x00, 0x00, 0x01 };
                    if (!videostream.is_open())
                    {
                        videostream.open((szPrefixMediaFilename + ".h264").c_str(), std::fstream::out | std::fstream::binary);
                    }
                    assert(videostream.is_open());
                    videostream.seekg(0, std::fstream::end);
                    int iFileLength = (int)videostream.tellg();
                    videostream.seekg(0, std::fstream::cur);
                    // 0x67 SPS - 0x68 PPS nal unit
                    if (iFileLength == 0)
                    {
                        unsigned short u16SpsLength = (((unsigned short)pVideoCodecCtx->extradata[6]) << 8) + pVideoCodecCtx->extradata[7];
                        unsigned short u16PpsLength = (((unsigned short)pVideoCodecCtx->extradata[8 + u16SpsLength + 1]) << 8) + pVideoCodecCtx->extradata[9 + u16SpsLength + 1];
                        char* szSps = (char*)malloc(u16SpsLength);
                        char* szPps = (char*)malloc(u16PpsLength);
                        memcpy(szSps, (void*)&pVideoCodecCtx->extradata[6+2], u16SpsLength);
                        memcpy(szPps, (void*)&pVideoCodecCtx->extradata[6 + 2 + u16SpsLength + 1 + 2], u16PpsLength);
                        videostream.write(szStart4Code, 4);
                        videostream.write(szSps, u16SpsLength);
                        videostream.write(szStart4Code, 4);
                        videostream.write(szPps, u16PpsLength);
                        free(szSps);
                        free(szPps);
                    }
                    // other nal unit
                    for (int i = 0, iStep = 0; i < sMediapacket.size; i += iStep)
                    {
                        uint32_t iUnitSize = ((uint32_t)sMediapacket.data[i] << 24) + ((uint32_t)sMediapacket.data[i + 1] << 16) + 
                                             ((uint32_t)sMediapacket.data[i + 2] << 8) + (uint32_t)sMediapacket.data[i + 3];
                        iStep = iUnitSize + 4;
                        sMediapacket.data[i] = sMediapacket.data[i + 1] = sMediapacket.data[i + 2] = 0x00;
                        sMediapacket.data[i + 3] = 0x01;
                        videostream.write((char*)&sMediapacket.data[i], (std::streamsize)iStep);
                        std::cout << "write to file : pts  " << sMediapacket.pts * av_q2d(pVideoStream->time_base) << " size " << iStep << std::endl;
                    }
                }
            }
            // audio decode
            if (sMediapacket.stream_index == pAudioStream->index)
            {
                iRet = avcodec_send_packet(pAudioCodecCtx, &sMediapacket);
                if (iRet != 0)
                {
                    av_strerror(iRet, szErrorBuffer, AV_ERROR_MAX_STRING_SIZE);
                    std::cout << "warning avcodec_send_packet failed " << szErrorBuffer << std::endl;
                    break;
                }
                iRet = avcodec_receive_frame(pAudioCodecCtx, pMediaFrame);
                if (iRet != 0)
                {
                    av_strerror(iRet, szErrorBuffer, AV_ERROR_MAX_STRING_SIZE);
                    std::cout << "warning avcodec_receive_frame failed " << szErrorBuffer << std::endl;
                    break;
                }
                // write aac stream
                if (pAudioCodecCtx->codec_id == AV_CODEC_ID_AAC)
                {
                    if (!audiostream.is_open())
                    {
                        audiostream.open((szPrefixMediaFilename + ".aac").c_str(), std::fstream::out | std::fstream::binary);
                    }
                    assert(audiostream.is_open());
                    int rates[] = { 96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000,7350,-1,-1,-1 };
                    int iProfile = pAudioStream->codecpar->profile;
                    int iChannel = pAudioStream->codecpar->channels;
                    int iSamplerate = pAudioStream->codecpar->sample_rate;
                    int iSamplerateIndex = 0;
                    for (auto rate : rates)
                    {
                        if(iSamplerate == rate) break;
                        iSamplerateIndex++;
                    }
                    int iAdtsLength = 7;
                    char* pAdts = (char*)malloc(iAdtsLength * sizeof(char));
                    int iSize = sMediapacket.size + iAdtsLength;
                    pAdts[0] = (char)0xff;
                    pAdts[1] = (char)0xf1;
                    pAdts[2] = (((iProfile - 1) << 6) + (iSamplerateIndex << 2) + (iChannel >> 2));
                    pAdts[3] = (((iChannel & 3) << 6) + (iSize >> 11));
                    pAdts[4] = ((iSize & 0x7ff) >> 3);;
                    pAdts[5] = (((iSize & 7) << 5) + 0x1f);
                    pAdts[6] = (char)0xfc;
                    audiostream.write(pAdts, (std::streamsize)iAdtsLength);
                    audiostream.write((char*)sMediapacket.data, (std::streamsize)sMediapacket.size);
                    free(pAdts);
                }
                // write pcm stream
                if(false)
                {
                    if (!audiobigstream.is_open())
                    {
                        audiobigstream.open((szPrefixMediaFilename + ".pcm").c_str(), std::fstream::out | std::fstream::binary);
                    }
                    assert(audiobigstream.is_open());
                    int iChannel = pAudioStream->codecpar->channels;
                    audiobigstream.write((char*)&pMediaFrame->data[0], (std::streamsize)pMediaFrame->linesize[0] / iChannel);
                    //std::cout << "write to file : pts  " << sMediapacket.pts * av_q2d(pAudioStream->time_base) << " size " << sMediapacket.size << std::endl;
                }
            }
            av_packet_unref(&sMediapacket);
        } while (!g_bStop);

        if (videostream.is_open())
        {
            videostream.close();
        }
        if (audiostream.is_open())
        {
            audiostream.close();
        }
        if (audiobigstream.is_open())
        {
            audiobigstream.close();
        }
        av_frame_free(&pMediaFrame);
        av_packet_unref(&sMediapacket);
        // close decoder
        avcodec_close(pAudioCodecCtx);
        avcodec_close(pVideoCodecCtx);
        // AVCodecContext
        avcodec_free_context(&pAudioCodecCtx);
        pAudioCodecCtx = nullptr;
        avcodec_free_context(&pVideoCodecCtx);
        pVideoCodecCtx = nullptr;
        // AVFormatContext
        avformat_close_input(&pMediaFormatCtx);
        pMediaFormatCtx = nullptr;
    }while(false);

    std::cout << "End ." << std::endl;

    return iRet;
}

windows build

@echo off

pushd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\Tools\"
call VsDevCmd.bat -arch=amd64
popd

cl /c /I. /Iffmpeg\include /DWIN32 /W3 /WX- /GS /GR /Zi /Od /MDd /EHsc /nologo main.cpp
link main.obj /LIBPATH:. /LIBPATH:.\ffmpeg\lib /DYNAMICBASE avformat.lib avcodec.lib avutil.lib /DEBUG /MACHINE:X64 /SUBSYSTEM:CONSOLE /PDB:main.pdb /OUT:main.exe
pause