【音视频开发(六)】---MP4文件读取,提取H264裸流包

接口:

  static int OpenInputFile(MP4FileHandle &hMP4File, MP4TrackId &m_videoTrackId, uint32_t &numSamples, const char *fileName);
    static int ReadConfigData(MP4FileHandle &hMP4File, MP4TrackId &m_videoTrackId,unsigned char **sps, int *spsSize, unsigned char **pps, int *ppsSize);
    static void CloseInputFile(MP4FileHandle &hMP4File);
    static int ReadH264Data(MP4FileHandle &hMP4File, MP4TrackId &m_videoTrackId, uint32_t numSamples, int  sampleId, unsigned char** ppData,int *size, int cfgsize);
    static void ReleaseConfigData(unsigned char **sps, unsigned char **pps);
    static void ReleaseH264Data(unsigned char** ppData);

打开输入文件,查找视频轨:


int Mp4v2Util::OpenInputFile(MP4FileHandle &hMP4File, MP4TrackId &m_videoTrackId, uint32_t &numSamples, const char *fileName)
{
    if (hMP4File == NULL) {
        DM_NATIVE_ERR_PRINT("open file %s.\n", fileName);
        hMP4File = MP4Read(fileName);
        if (hMP4File == MP4_INVALID_FILE_HANDLE)    {
            DM_NATIVE_ERR_PRINT("open file failed %s.\n", fileName);
            return -1;
        }
        uint32_t trackId = MP4_INVALID_TRACK_ID;
        uint32_t numOfTracks = MP4GetNumberOfTracks(hMP4File);
        DM_NATIVE_ERR_PRINT("numOfTracks=%d\n", numOfTracks);
        // find video track
        for (uint32_t tmpTrackId = 1; tmpTrackId <= numOfTracks; tmpTrackId++) {

            const char* trackType = MP4GetTrackType(hMP4File, tmpTrackId);
            DM_NATIVE_ERR_PRINT("%d trackType=%s\n", tmpTrackId, trackType);
            if (MP4_IS_VIDEO_TRACK_TYPE(trackType)) {
                trackId = tmpTrackId;
                break;
            }
        }
        if (trackId == MP4_INVALID_TRACK_ID) {
            DM_NATIVE_ERR_PRINT("Can't find video track\n");
            CloseOutputFile(hMP4File);
            return -1;
        }
        m_videoTrackId = trackId;
        //DM_NATIVE_ERR_PRINT("m_videoTrackId=%d\n", m_videoTrackId);
        numSamples = MP4GetTrackNumberOfSamples(hMP4File, m_videoTrackId);
    }
    return 0;
}

读取视频文件的SPS和PPS:


int Mp4v2Util::ReadConfigData(MP4FileHandle &hMP4File, MP4TrackId &m_videoTrackId,unsigned char **sps, int *spsSize, unsigned char **pps, int *ppsSize)
{
    // read sps/pps
    uint8_t **seqheader;
    uint8_t **pictheader;
    uint32_t *pictheadersize;
    uint32_t *seqheadersize;
    uint32_t ix;
    const char nalHeader[] = {0x00, 0x00, 0x00, 0x01};
    MP4GetTrackH264SeqPictHeaders(hMP4File, m_videoTrackId, &seqheader, &seqheadersize, &pictheader,
                                  &pictheadersize);


    int size = 4;
    //sps
    for (ix = 0; seqheadersize[ix] != 0; ix++) {
        size += seqheadersize[ix];
    }

    *sps = (uint8_t *)malloc(size);
    uint8_t *spsBase =  *sps;
    memcpy(spsBase, nalHeader, 4);
    spsBase += 4;
    for (ix = 0; seqheadersize[ix] != 0; ix++) {
        memcpy(spsBase,seqheader[ix], seqheadersize[ix]);
        spsBase += seqheadersize[ix];
        free(seqheader[ix]);
    }
    free(seqheader);
    free(seqheadersize);
    *spsSize = size;
    //pps
    size = 4;
    for (ix = 0; pictheadersize[ix] != 0; ix++) {
        size += pictheadersize[ix];
    }
    *pps = (uint8_t *)malloc(size);
    uint8_t *ppsBase =  *pps;
    memcpy(ppsBase, nalHeader, 4);
    ppsBase += 4;

    for (ix = 0; pictheadersize[ix] != 0; ix++) {
        memcpy(ppsBase, pictheader[ix], pictheadersize[ix]);
        ppsBase += pictheadersize[ix];
        free(pictheader[ix]);
    }
    free(pictheader);
    free(pictheadersize);
    *ppsSize = size;
    return 0;
}

读取H264裸流编码帧:


int Mp4v2Util::ReadH264Data(MP4FileHandle &hMP4File, MP4TrackId &m_videoTrackId, uint32_t numSamples, int sampleId, unsigned char** ppData,int *size, int cfgsize)
{
    // read samples

    uint8_t* pSample = NULL;
    uint32_t sampleSize = 0;
    const char nalHeader[] = {0x00, 0x00, 0x00, 0x01};

    
        if (!MP4ReadSample(hMP4File, m_videoTrackId, sampleId, &pSample, &sampleSize)) {
            DM_NATIVE_ERR_PRINT("read sampleId %u error\n", sampleId);
            if (pSample != NULL) {
                free(pSample);
                pSample = NULL;
            }
            return -1;
        }

    if (pSample != NULL) {

        uint32_t pktsize = pSample[0];
        pktsize <<= 8;
        pktsize |= pSample[1];
        pktsize <<= 8;
        pktsize |= pSample[2];
        pktsize <<= 8;
        pktsize |= pSample[3];
       // DM_NATIVE_DEBUG_PRINT("test %02X %02X %02X %02X pktsize=%d sampleSize=%d\n",pSample[0],pSample[1],pSample[2],pSample[3], pktsize, sampleSize );
        if((pktsize + 4) == sampleSize) {
            memcpy(pSample, nalHeader, 4);
            *ppData = pSample;
            *size = sampleSize;
        } else if((pktsize + 4) < sampleSize) {
            int leftsize = sampleSize-cfgsize;
            if (leftsize > 4) {
                uint32_t pktsize1 = pSample[cfgsize+0];
                pktsize1 <<= 8;
                pktsize1 |= pSample[cfgsize+1];
                pktsize1 <<= 8;
                pktsize1 |= pSample[cfgsize+2];
                uint32_t pktsize2 = pktsize1;
                pktsize1 <<= 8;
                pktsize1 |= pSample[cfgsize+3];
                if ((pktsize2+3) == leftsize) {
                    memcpy(&pSample[cfgsize], &nalHeader[1], 3);
                }else if ((pktsize2+4) == leftsize) {
                    memcpy(&pSample[cfgsize], &nalHeader[0], 4);
                }
            }
            memcpy(pSample, nalHeader, 4);
            *ppData = pSample;
            *size = sampleSize;
        } else {
            DM_NATIVE_ERR_PRINT("ReadH264Data ERR %02X %02X %02X %02X\n",pSample[0],pSample[1],pSample[2],pSample[3] );
            free(pSample);

            *ppData = NULL;
            *size = 0;
        }
        return 0;
    }

  
    return -2;
}

释放数据:


void Mp4v2Util::ReleaseConfigData(unsigned char **sps, unsigned char **pps)
{
    if(*sps) {
        free(*sps);
        *sps = NULL;
    }
    if(*pps) {
        free(*pps);
        *pps = NULL;
    }
}


void Mp4v2Util::ReleaseH264Data(unsigned char** ppData)
{
    if(*ppData) {
        free(*ppData);
        *ppData = NULL;
    }
}

关闭文件:

void Mp4v2Util::CloseInputFile(MP4FileHandle &hMP4File)
{
    DM_NATIVE_DEBUG_PRINT("CloseOutputFile end\n");
    if(hMP4File) MP4Close(hMP4File, 0);
    hMP4File = NULL;
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可以使用FFmpeg库来保存视频文件,具体实现可以参考以下代码: ```c++ #include <iostream> #include <string> #include <sstream> #include <fstream> #include <chrono> #include <thread> #include <opencv2/opencv.hpp> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> using namespace std; using namespace cv; int main(int argc, char* argv[]) { // 初始化FFmpeg库 av_register_all(); avcodec_register_all(); // 打开视频文件 string filename = "test.mp4"; AVFormatContext* format_ctx = nullptr; if (avformat_open_input(&format_ctx, filename.c_str(), nullptr, nullptr) != 0) { cerr << "Failed to open video file " << filename << endl; return -1; } // 查找视频 int video_stream_index = -1; for (unsigned int i = 0; i < format_ctx->nb_streams; i++) { if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { video_stream_index = i; break; } } if (video_stream_index == -1) { cerr << "Failed to find video stream in " << filename << endl; avformat_close_input(&format_ctx); return -1; } // 获取视频的解码器 AVCodec* codec = avcodec_find_decoder(format_ctx->streams[video_stream_index]->codecpar->codec_id); if (codec == nullptr) { cerr << "Failed to find codec for video stream in " << filename << endl; avformat_close_input(&format_ctx); return -1; } // 打开解码器 AVCodecContext* codec_ctx = avcodec_alloc_context3(codec); if (avcodec_parameters_to_context(codec_ctx, format_ctx->streams[video_stream_index]->codecpar) != 0) { cerr << "Failed to copy codec parameters to decoder context in " << filename << endl; avcodec_free_context(&codec_ctx); avformat_close_input(&format_ctx); return -1; } if (avcodec_open2(codec_ctx, codec, nullptr) != 0) { cerr << "Failed to open codec for video stream in " << filename << endl; avcodec_free_context(&codec_ctx); avformat_close_input(&format_ctx); return -1; } // 创建视频文件 string output_filename = "output.mp4"; AVFormatContext* output_format_ctx = nullptr; if (avformat_alloc_output_context2(&output_format_ctx, nullptr, nullptr, output_filename.c_str()) != 0) { cerr << "Failed to create output format context for " << output_filename << endl; avcodec_free_context(&codec_ctx); avformat_close_input(&format_ctx); return -1; } // 添加视频 AVStream* output_stream = avformat_new_stream(output_format_ctx, nullptr); if (output_stream == nullptr) { cerr << "Failed to create output video stream for " << output_filename << endl; avcodec_free_context(&codec_ctx); avformat_close_input(&format_ctx); avformat_free_context(output_format_ctx); return -1; } output_stream->id = output_format_ctx->nb_streams - 1; if (avcodec_parameters_copy(output_stream->codecpar, format_ctx->streams[video_stream_index]->codecpar) != 0) { cerr << "Failed to copy codec parameters to output video stream in " << output_filename << endl; avcodec_free_context(&codec_ctx); avformat_close_input(&format_ctx); avformat_free_context(output_format_ctx); return -1; } output_stream->codecpar->codec_tag = 0; if (avformat_write_header(output_format_ctx, nullptr) != 0) { cerr << "Failed to write output file header for " << output_filename << endl; avcodec_free_context(&codec_ctx); avformat_close_input(&format_ctx); avformat_free_context(output_format_ctx); return -1; } // 分配解码缓存 AVFrame* frame = av_frame_alloc(); AVPacket packet; av_init_packet(&packet); // 读取视频帧并保存 while (av_read_frame(format_ctx, &packet) == 0) { if (packet.stream_index == video_stream_index) { if (avcodec_send_packet(codec_ctx, &packet) != 0) { cerr << "Failed to send packet to decoder in " << filename << endl; break; } while (avcodec_receive_frame(codec_ctx, frame) == 0) { // 将YUV图像转换为BGR图像 Mat bgr_image(frame->height, frame->width, CV_8UC3); SwsContext* sws_ctx = sws_getContext(frame->width, frame->height, (AVPixelFormat)frame->format, frame->width, frame->height, AV_PIX_FMT_BGR24, SWS_BILINEAR, nullptr, nullptr, nullptr); uint8_t* data[AV_NUM_DATA_POINTERS] = { 0 }; data[0] = bgr_image.data; int linesize[AV_NUM_DATA_POINTERS] = { 0 }; linesize[0] = bgr_image.step; sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, data, linesize); sws_freeContext(sws_ctx); // 保存BGR图像 AVPacket output_packet; av_init_packet(&output_packet); output_packet.data = nullptr; output_packet.size = 0; avcodec_send_frame(codec_ctx, frame); while (avcodec_receive_packet(codec_ctx, &output_packet) == 0) { output_packet.stream_index = output_stream->id; av_write_frame(output_format_ctx, &output_packet); av_packet_unref(&output_packet); } // 显示BGR图像 imshow("BGR Image", bgr_image); waitKey(1); } } av_packet_unref(&packet); } // 释放资源 av_write_trailer(output_format_ctx); avcodec_free_context(&codec_ctx); avformat_close_input(&format_ctx); avformat_free_context(output_format_ctx); av_frame_free(&frame); return 0; } ``` 这段代码可以读取一个视频文件,将其中的每一帧转换为BGR格式的图像,并保存为另一个视频文件。其中,使用了OpenCV库来显示BGR图像,使用了FFmpeg库来读取和保存视频文件
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值