FFmpeg学习笔记(二)

作者: Hunter琼 | 来源:发表于2021-02-22 12:18 被阅读0次

    libavformat:实现流协议,容器格式及IO访问,下面代码使用vim编写,提高记忆.

    1 删除文件和文件的重用名

     #include <libavformat/avformat.h>
    
     int main(int argc, char *argv[]){
    
    int ret = 0;
    
    //删除路径可以是个url
    
    ret = avpriv_io_delete("./test.txt");
    
    if(ret <0){
    
    av_log(NULL,AV_LOG_ERROR,"文件删除失败\n");
    
    return -1;
    }
    
        // AV_LOG_ERROR //级别最高
       //AV_LOG_WARNING
       //AV_LOG_INFO
      //AV_LOG_DEBUG
    
       av_log(NULL,AV_LOG_DEBUG,"文件删除成功\n");
    
     // 重用名
     int removeRet;
    removeRet = avpriv_io_move("1.txt","2.txt");
    
     if(removeRet < 0){
         av_log(NULL,AV_LOG_INFO,"1.txt文件重用名失败   \n");
     }else{
         av_log(NULL,AV_LOG_INFO,"1.txt文件改成了     2.txt\n");
       }
        return 0;
      }
    

    在mac下 使用clang对上述代码进行编译.
    clang -g -o ffmpeg_file DelectmoveFFmpeg.c `pkg-config --libs libavformat`

    2 文件目录的操作

    #include<stdio.h>
    //logo
    #include<libavutil/log.h>
    #include<libavformat/avformat.h>
    int main(int argc, char *argv[]){
     printf("ffmpeg 文件目录的操作");
    
      // 获取上下文
      AVIODirContext *ctx = NULL;
      AVIODirEntry *entry = NULL;
      av_log_set_level(AV_LOG_INFO);
    // 打开目录
      int isSucceed = avio_open_dir(&ctx,"./",NULL);
      if(isSucceed < 0 ){
       av_log(NULL,AV_LOG_ERROR,"目录打开失败");
       return -1;
    }
     // 读取每一项
      while(1){
      isSucceed = avio_read_dir(ctx,&entry);
      if(isSucceed < 0){
       av_log(NULL,AV_LOG_ERROR,"不能读取目录:%s\n",av_err2str(isSucceed));
        //防止内存泄漏
        goto __fail;
       // return -1;
    }
      if(entry == NULL){
       break;
    }
      av_log(NULL,AV_LOG_INFO,"%12"PRId64" %s\n",entry->size,entry->name);
      // 释放entry
      avio_free_directory_entry(&entry);
    }
    // 释放
    __fail:
    avio_close_dir(&ctx);
     return 0;
    }
         
    

    clang编译:
    clang -g -o testcatalogue FFmpegCatalogueHandle.c `pkg-config --libs libavformat,libavutil`

    运行结果:

    ./testcatalogue
            6148 .DS_Store
           50320 testcatalogue
              96 testcatalogue.dSYM
             738 FFmpegCatalogueHandle.c
    

    3 FFmpeg 打印音视频的信息

    #include "libavutil/log.h"
    #include "libavformat/avformat.h"
    
    int main(int avgr ,char *argv[]){
        
        AVFormatContext *ctx = NULL;
        av_log_set_level(AV_LOG_INFO);
        av_register_all();
        
        int ret = avformat_open_input(&ctx,"./scale.mp4",NULL,NULL);
        if(ret >= 0){
        // 音视频信息
         av_dump_format(ctx,0,"./scale.mp4",0);     
    
        }else{
         av_log(NULL,AV_LOG_ERROR,"不能打开文件:%s\n",av_err2str(ret));
        }
        
        avformat_close_input(&ctx);
    
      return 0;
    
    }
    

    编译 clang -g -o testmeta VideoAudioMeta.c `pkg-config --libs libavformat,libavutil

    image.png

    需要怎么改进程序,解除这个警告呢??? 解决方法如下:

    #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
    av_register_all();
    #endif
    #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
    avcodec_register_all();
    #endif
    

    4 抽取音频流

    #include "libavutil/log.h"
    #include "libavformat/avformat.h"
    
    int main(int argc, char *argv[]){
        
        char *src = NULL;
        char *dst = NULL;
    
        AVFormatContext *ctx = NULL;
        
        av_log_set_level(AV_LOG_INFO);
       
        #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
          av_register_all();
        #endif
        #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
           avcodec_register_all();
        #endif
        
        // 读取参数
        if(argc < 3){
         av_log(NULL,AV_LOG_ERROR,"传入的参数不对");
         return -1;
        }    
        // 赋值
        src = argv[1];
        dst = argv[2];
        if(!src || !dst){
           av_log(NULL,AV_LOG_ERROR,"参数有误");
           return -1;
        }
        
    
        int ret = avformat_open_input(&ctx,src,NULL,NULL);
        
        if(ret >= 0){
         av_dump_format(ctx,0,src,0);
        }else{
         av_log(NULL,AV_LOG_ERROR,"不能打开文件%s\n",av_err2str(ret)); 
        }
        // 打开写入文件 二进制文件 aac
        FILE *dst_fd = fopen(dst,"wb");
        
        if(!dst_fd){
         av_log(NULL,AV_LOG_ERROR,"文件打开失败\n");
         avformat_close_input(&ctx);
         return -1;     
        }    
        
        AVPacket pkt;
        int audio_index;
        int  len;
        // 获取流
        ret = av_find_best_stream(ctx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
        if(ret < 0){
         av_log(NULL,AV_LOG_ERROR,"音频流获取失败\n");
         avformat_close_input(&ctx);
         return -1;
        }
        
        audio_index = ret;
        av_init_packet(&pkt);
        //  获得一帧音频的压缩数据
        while(av_read_frame(ctx,&pkt) >=0){
         if(pkt.stream_index == audio_index){
           // 写入流 是压缩后的流 我们需要最好的流写进去
            len = fwrite(pkt.data,1,pkt.size,dst_fd);
            if(len != pkt.size){
             av_log(NULL,AV_LOG_WARNING,"警告,写入数据出现未知的问题\n");
            }
         }
         //回收
        av_packet_unref(&pkt);
       }
        avformat_close_input(&ctx);
        
        if(dst_fd){
          fclose(dst_fd);   
        }   
    
    
    
        return 0;
    }
    

    编译完成后运行 ,需要传两个参数才可以运行: scale.mp4 ,getaudio.aac
    ./testAudioMeta scale.mp4 getaudio.aac

    注意:这里的aac文件无法播放 .如果需要播放,似乎需要修改程序. 比如使用ffplay播放,会报错:

    ffplay getaudio.aac

    image.png

    5 抽取视频流

    #include <libavutil/log.h>
    #include <libavformat/avformat.h>
    #include <stdio.h>
    
    #ifndef AV_WEB32
    # define AV_WB32(p,val) do {         \
       uint32_t d = (val);               \
       ((uint8_t *)(p))[3] = (d);        \
       ((uint8_t *)(p))[2] = (d)>>8;     \
       ((uint8_t *)(p))[1] = (d)>>16;    \
       ((uint8_t *)(p))[0] = (d)>>24;    \
    }while(0)
    #endif
    
    #ifndef AV_RB16
    #  define AV_RB16(x)                 \
    ((((const uint8_t *)(x))[0] << 8) |  \
      ((const uint8_t *)(x))[1])
    #endif
    
    
    static int alloc_and_copy(AVPacket *out,const uint8_t *sps_pps,uint32_t sps_pps_size,const uint8_t *in ,uint32_t in_size){
      
       uint32_t offset  = out ->size;
    
       uint8_t nal_header_size =  offset ? 3: 4;
       int err ;
       // 扩容av_grow_packet
       if((err = av_grow_packet(out,sps_pps_size + in_size + nal_header_size)) <0){
         return err;
       }
       // 先copy sps和pps 
       if(sps_pps){
           memcpy(out->data + offset,sps_pps,sps_pps_size);
        }
        // 在拷贝特征码 startcode
        memcpy(out->data + sps_pps_size + nal_header_size +
          offset,in,in_size);
       
        if(!offset){
        // 0001 没有sps和pps
          AV_WB32(out->data + sps_pps_size,1);
        }else{
        // sps pps 后面三位设置成 001
          (out->data + offset + sps_pps_size)[0] = 0;
           (out->data + offset + sps_pps_size)[1] = 0;
           (out->data + offset + sps_pps_size)[2] = 1;
        }
        
      // for(int i = 0;i < nal_header_size;i++){
        // (out->data+offset + sps_pps_size)[i] = i == nal_header_size - 1?1:0;
      // }
      // memcpy(out->data + sps_pps_size + nal_header_size +
      //  offset,in,in_size);
    
        return 0;
    }
    
    // 读取sps/pps的数据,并拷贝
    int h264_extraData_to_Annexb(const uint8_t *codec_Data,const int codecDataSize,AVPacket *out_extraData,int padding){
    
    // sps/pps的数据长度
      uint16_t unitSize;
    //所有sps/pps 加上其特征码后总长度
      uint64_t totalSize = 0;
      
      for(int i = 0; i < codecDataSize;i++){
    
        printf("%02x  ",*(codec_Data + i));
      }
      
      uint8_t *out = NULL;
      uint8_t unit_nb;
      uint8_t sps_done = 0;
      uint8_t sps_seen = 0;
      uint8_t  pps_seen = 0;
      // sps数据偏移
      uint8_t  sps_offset = 0;
      //pps数据偏移
      uint8_t pps_offset = 0;
      //过滤掉前4个字节
      const uint8_t *extraData = codec_Data + 4  ;
    
      static const uint8_t nalu_header[4] = {0,0,0,1};
      // 2位 
      int length_size = (*extraData++ & 0x3) + 1;
    
      sps_offset = pps_offset = -1;
      // sps和pps的个数
      unit_nb = *extraData++ & 0x1f;
    
      if(!unit_nb){
       goto pps;
      }else{
        sps_offset = 0;
        sps_seen = 1;
      }
      while(unit_nb--){
        int err;
       // unitSize = (extraData[0] << 8) | extraData[1];
        unitSize =  AV_RB16(extraData);
        totalSize += unitSize + 4;
        if(totalSize > INT_MAX - padding){
          av_log(NULL,AV_LOG_ERROR,"输入数据流太大,视频流损坏或者非法的MP4/AVCC 比特流\n");
          av_free(out);
          return AVERROR(EINVAL);
        }
    
        if(extraData + 2 + unitSize > codecDataSize + codec_Data ){
         
          av_log(NULL,AV_LOG_ERROR,"数据异常\n");
          av_free(out);
          return AVERROR(EINVAL);
    
        }
    //    av_log(NULL,AV_LOG_ERROR,"是否执行\n");
        if((err = av_reallocp(&out,totalSize + padding)) < 0)
           return err;
         //拷贝nalu_header
         memcpy(out + totalSize - unitSize - 4,nalu_header,4 );
         // 拷贝spspps
         memcpy(out + totalSize - unitSize ,extraData + 2,unitSize);
    
         extraData += 2 + unitSize;
    
     pps:
          if(!unit_nb && !sps_done++){
            unit_nb = *extraData++;
        if(unit_nb){
             pps_offset = totalSize;
         pps_seen = 1;
        }
       }
    
      }
    
      if(out){
        memset(out + totalSize,0,padding);
      }
    
      if(!sps_seen){
       av_log(NULL,AV_LOG_WARNING,"警告没有sps数据");
      }
    
      if(!pps_seen){
        av_log(NULL,AV_LOG_WARNING,"警告没有pps数据");
      }
    
      out_extraData->data = out;
      out_extraData->size = totalSize;
    
      return length_size;
    
    }
    
    
    //为包数据添加始码startcode ,并将获取的sps/pps等信息写入文件
    int h264_videotoannexb(AVFormatContext *ctx, AVPacket *in,FILE *dst_fd){
    
        AVPacket *out = NULL;
        AVPacket spspps_pkt;
        int len;
        uint8_t unit_type;
        int32_t nal_size;
        uint32_t cumul_size = 0;
        const uint8_t *buf;
        const uint8_t *buf_end;
        int  buf_size;
        int ret = 0,i;
    
        out = av_packet_alloc();
        buf = in->data;
        buf_size = in->size;
        //缓冲区的最后位置
        buf_end = in->data + in->size;
        do{
          ret = AVERROR(EINVAL);
         // 数据没有内容      
          if(buf + 4 > buf_end)
            goto fail;
          //取出packet前面4个字节和32位的高位地址和32位地位地址进行交换,得到nal_size大小
        for(nal_size = 0,i = 0; i < 4; i++)
              nal_size = (nal_size << 8) | buf[i];
          // 向后移4个字节
         buf += 4;
    
           // 第一个字节的后五位,就是单元类型
         unit_type = *buf & 0x1f;
    
         if(nal_size > buf_end - buf || nal_size < 0)
          goto fail;
        
           //获取sps/pps
            if(unit_type == 5){
              h264_extraData_to_Annexb(ctx ->streams[in->stream_index] ->codec ->extradata,ctx ->streams[in->stream_index]->codec->extradata_size,&spspps_pkt,AV_INPUT_BUFFER_PADDING_SIZE);
             //新增特征吗 startcode
              if((ret=alloc_and_copy(out,spspps_pkt.data,spspps_pkt.size,buf,nal_size)) <0 ){
               goto fail;
            }
        }else{
            if((ret = alloc_and_copy(out,NULL,0,buf,nal_size)) < 0)
           goto fail;
        }
       
         len = fwrite(out ->data,1,out->size,dst_fd);
         if(len != out -> size){
           av_log(NULL,AV_LOG_DEBUG,"警告,写入文件长度不等于包大小 %d, %d\n",len,    out->size);
         }
         fflush(dst_fd);
           
        buf += nal_size;
       cumul_size += nal_size + 4;
    
        }while(cumul_size < buf_size);
    
      //  len = fwrite(out ->data,1,out->size,dst_fd);
    
       // if(len != out -> size){
        //  av_log(NULL,AV_LOG_DEBUG,"警告,写入文件长度不等于包大小 %d, %d\n",len,out->size);
        //}
    
        // fflush(dst_fd);
    
         fail:
            av_packet_free(&out);
        
         return ret;
    
    
    
    }
    
    int main(int argc,char *argv[]){
      // 音视频文件
      char *src = NULL;
      // 输出文件
      char *dst = NULL;
      
      av_log_set_level(AV_LOG_INFO);
     
      #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
          av_register_all();
        #endif
        #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
           avcodec_register_all();
        #endif
      // 必须传入音频文件和视频流输出文件
        if(argc <3){
        
         av_log(NULL,AV_LOG_ERROR,"参数传入数量错误!\n");
         return -1;   
        } 
        
         src  = argv[1];
         dst = argv[2];
         
         if(!src || !dst){
         av_log(NULL,AV_LOG_ERROR,"传入参数有误,请检查\n");
         return -1;
        }
        AVFormatContext *ctx = NULL;
       // 错误描述
        char *errbuf;
        int ret = avformat_open_input(&ctx,src,NULL,NULL);
        FILE *dst_fd = fopen(dst,"wb");
        if(!dst_fd){
        av_log(NULL,AV_LOG_DEBUG,"不能打开文件%s\n",dst);
        return -1;
        }
        if(ret < 0){
        av_strerror(ret,errbuf,1024);
        av_log(NULL,AV_LOG_DEBUG,"不能打开资源文件:%s,%d(%s)\n",src,ret,errbuf);
        return -1;
        }
        // 打开资源文件,获取最好的流
        ret = avformat_find_stream_info(ctx,NULL);
        if(ret < 0){
         av_strerror(ret,errbuf,1024);
        av_log(NULL,AV_LOG_DEBUG,"不能打开资源文件:%s,%d(%s)\n",src,ret,errbuf);
        return -1;
        }
        av_dump_format(ctx,0,src,0);
        //初始化packet
         AVPacket pkt ;
         av_init_packet(&pkt);
         pkt.data = NULL;
         pkt.size = 0;
         // 视频流的index
         int video_stream_index = av_find_best_stream(ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
         if(video_stream_index < 0){
         av_log(NULL,AV_LOG_DEBUG,"不能找到%s流,输入到文件%s\n",av_get_media_type_string(AVMEDIA_TYPE_VIDEO),src); 
          return AVERROR(EINVAL);
        }
        //取出流中数据包  并处理
        while(av_read_frame(ctx,&pkt) >= 0){
          if(pkt.stream_index == video_stream_index){
              h264_videotoannexb(ctx,&pkt,dst_fd);
          }
          av_packet_unref(&pkt);
        }
        
        if(dst_fd){
            fclose(dst_fd);
            
        }
    
        avformat_close_input(&ctx);
     return 0;
    }
    

    6 mp4转化为flv

    #include <libavformat/avformat.h>
    #include <libavutil/log.h>
    #include <libavutil/timestamp.h>
     //#include <stdio.h>
    //log_packet' is invalid in C99 [-Werror,-Wimplicit-function-declaration] 函
      //  数没有被定义,不能调用隐式函数
    //*** error for object 0x1: pointer being freed was not allocated 野指针
    
    
    static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt,     const char *tag);
    
    
    int main(int argc,char *argv[]){
    
      av_log_set_level(AV_LOG_INFO);
    
      #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
      av_register_all();
     #endif
     #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
      avcodec_register_all();
     #endif
    
      AVOutputFormat *ofmt = NULL;
      
      AVFormatContext *ifmt_ctx = NULL;
      AVFormatContext *ofmt_ctx = NULL;
      AVPacket pkt;
    
      const char *in_fileName, *out_fileName;
    
      if(argc < 3){
       
       av_log(NULL,AV_LOG_ERROR,"入参错误");
       return -1;
      }
      
      in_fileName = argv[1];
      out_fileName = argv[2];
      
      int ret;
      // 打开多媒体文件
      if((ret = avformat_open_input(&ifmt_ctx,in_fileName,0,0)) < 0){
        
        av_log(NULL,AV_LOG_ERROR,"多媒体文件打开失败\n");
       
        goto fail;
      }
     
      if((ret = avformat_find_stream_info(ifmt_ctx,0)) < 0){
        
        av_log(NULL,AV_LOG_ERROR,"多媒体文件无法获取流信息\n");
    
        goto fail;
      }
      
      // dump 信息
      av_dump_format(ifmt_ctx,0,in_fileName,0);
    
      // 创建输出上下文
       
      avformat_alloc_output_context2(&ofmt_ctx,NULL,NULL,out_fileName);
      
      if(!ofmt_ctx){
        
        av_log(NULL,AV_LOG_ERROR,"创建输出上下文失败\n");
        
        ret = AVERROR_UNKNOWN;
    
        goto fail;
      }
    
      int stream_index = 0;
      int *stream_mapping = NULL;
      int stream_mapping_size = 0;  
      
      stream_mapping_size = ifmt_ctx -> nb_streams;
      stream_mapping    =  av_mallocz_array(stream_mapping_size,sizeof(*stream_mapping));
      
      if(!stream_mapping){
        ret = AVERROR(ENOMEM);
        goto fail;
      }
      
      ofmt = ofmt_ctx -> oformat;
    
      for(int i = 0; i < ifmt_ctx ->nb_streams; i++){
         // copy输入音频流
         AVStream *out_stream;
         AVStream *in_stream = ifmt_ctx -> streams[i];
         AVCodecParameters *in_codecpar = in_stream->codecpar;
    
         if(in_codecpar -> codec_type != AVMEDIA_TYPE_AUDIO && in_codecpar ->codec_type != AVMEDIA_TYPE_VIDEO && in_codecpar ->codec_type != AVMEDIA_TYPE_SUBTITLE){
             stream_mapping[i] = -1;
            continue;
        }
        
        stream_mapping[i] =  stream_index++;
        out_stream = avformat_new_stream(ofmt_ctx,NULL);
    
        if(!out_stream){
           av_log(NULL,AV_LOG_ERROR,"错误的输出流\n");
           ret = AVERROR_UNKNOWN;
           goto fail;
        }
        
        ret = avcodec_parameters_copy(out_stream -> codecpar,in_codecpar );
        
        if(ret < 0){
          av_log(NULL,AV_LOG_ERROR,"编码器参数错误\n");
          goto fail;
        }
        
        out_stream->codecpar->codec_tag = 0;
      }
       av_dump_format(ofmt_ctx,0,out_fileName,1);
       
       if(!(ofmt -> flags & AVFMT_NOFILE)){
          
          ret = avio_open(&ofmt_ctx->pb,out_fileName,AVIO_FLAG_WRITE);
    
          if(ret < 0){
           av_log(NULL,AV_LOG_ERROR,"'%s' 文件不能打开\n",out_fileName);
           goto fail;
          }
    
       }
      
      // 写入haeder
       ret = avformat_write_header(ofmt_ctx,NULL);
        
       if(ret < 0){
          av_log(NULL,AV_LOG_ERROR,"写入head失败\n");
    
         goto fail;
       }
       
       while(1){
    
         AVStream *in_stream, *out_stream;
         ret = av_read_frame(ifmt_ctx,&pkt);
        
         if(ret < 0)
          break;
    
         in_stream = ifmt_ctx ->streams[pkt.stream_index];
         
          if(pkt.stream_index >= stream_mapping_size || stream_mapping[pkt.stream_index] < 0  ){
             av_packet_unref(&pkt);
            continue;
          }
         
          pkt.stream_index = stream_mapping[pkt.stream_index];
          out_stream = ofmt_ctx ->streams[pkt.stream_index];
           
          log_packet(ifmt_ctx, &pkt, "in");
    
        // 刻度转化 转化时间基
         pkt.pts = av_rescale_q_rnd(pkt.pts,in_stream->time_base,out_stream->time_base,AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
         pkt.dts = av_rescale_q_rnd(pkt.dts,in_stream->time_base,out_stream->time_base,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;
         log_packet(ofmt_ctx, &pkt, "out");
         ret = av_interleaved_write_frame(ofmt_ctx,&pkt);
         if(ret < 0){
           av_log(NULL,AV_LOG_ERROR,"error muxing packet\n");
           break;
         }
         av_packet_unref(&pkt);
     }
      av_write_trailer(ofmt_ctx);
      fail:
      avformat_close_input(&ifmt_ctx);
      
      if(ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
         avio_closep(&ofmt_ctx->pb);
    
       avformat_free_context(ofmt_ctx);
       av_freep(&stream_mapping);
    
       if(ret < 0 && ret != AVERROR_EOF){
          av_log(NULL,AV_LOG_ERROR,"文件解析出现异常:%s\n",av_err2str(ret));
          return -1;
       }
    
      return 0;
    }
    
    
    static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag)
    {
        AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
    
        printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
               tag,
               av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
               av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
               av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
               pkt->stream_index);
    }
    
    

    编译clang -g -o HandelFormat VideoHandelFormatCharge.c `pkg-config --libs libavformat libavutil libavcodec

    相关文章

      网友评论

        本文标题:FFmpeg学习笔记(二)

        本文链接:https://www.haomeiwen.com/subject/rovhfltx.html