ffmpeg之demux 解复用

今天学习解析媒体文件。

写了一个用例,解析MP4文件得到视频帧和音频帧,并分别保存到不同的文件。

照惯例,先学习,再代码。


学习

av_register_all

  1. /** 
  2.  * 初始化 libavformat,并且注册所有的合并器、解析器和协议。 
  3.  * 如果你不调用这个方法,你可以明确地选择你想要程序支持的格式。 
  4.  * 参照 av_register_input_format() 
  5.  * 参照 av_register_output_format() 
  6.  */  
  7. void av_register_all(void);  

avformat_open_input

  1.  /** 
  2.  * 打开一个输入流并且读取它的头部数据,这个编解码器不会被打开。 
  3.  * 这个流必须使用avformat_close_input()关闭。 
  4.  *  
  5.  * @参数 ps   指向一个用户提供的AVFormatContext(使用avformat_alloc_context分配的空间)。 
  6.  *          这个参数可以指向NULL,这样,该参数将会在方法内部被分配控件,并且写入ps。 
  7.  * 
  8.  *          注意,如果这个参数是用户提供的,方法调用失败后,AVFormatContext会被释放。 
  9.  * 
  10.  * @参数 url 被打开媒体流的url。对于媒体文件,它是媒体文件路径。 
  11.  * @参数 fmt 如果不为空,这个参数规定一个特定的输入格式。 
  12.  *           否则,输入格式将被自动检测。 
  13.  * @参数 options ... 
  14.  * 
  15.  * @成功返回0,失败返回负数。 
  16.  * 
  17.  * @注意,如果你想使用特定的IO,在分配格式上下文空间之前,设置它的pb字段 
  18.  */  
  19. int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);  

avformat_find_stream_info

  1. /* 读取媒体文件的包来获取流信息。这个方法对于没有头部的文件格式也是有用的,例如MPEG。 
  2. * 对于MPEG-2这种帧模型重复的类型,这个方法也会计算真实帧率。 
  3. * 
  4. * 该逻辑文件地址不被这个方法更改。 
  5. * 检查的数据包可能会被缓冲以用于以后处理。 
  6.  
  7. * @参数 ic 媒体文件句柄(格式上下文) 
  8. * @参数 options ... 
  9. * @成功返回值大于0 
  10. * @注意:这个方法不保证打开所有的编解码器,所以非空的options参数将会返回一个完全合理的行为。  
  11. * 
  12. * 为了让用户决定什么样的信息是他们需要的,我们不会浪费时间去获取用户不需要的东西。 
  13. */  
  14. nt avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);  

av_find_best_stream

  1. /* 在媒体文件中寻找最合适的流结构体(AVStream)。 
  2. * 如果参数decoder_ret为非空,av_find_best_stream将会流的编解码寻找默认解码器。 
  3. * 对于那些不能找到解码器的流来说,它会被忽略。 
  4. * @param ic 媒体文件句柄(格式上下文) 
  5. * @param type 流类型,视频(AVMEDIA_TYPE_VIDEO),音频(AVMEDIA_TYPE_AUDIO) 
  6. * @param wanted_stream_nb AVStream在AVFormatContext中streams的索引,调试证明最好填-1。 
  7. * @param related_stream -1 
  8. * @param decoder_ret null 
  9. * @param flags 0 
  10. * @return 成功返回值非负。 
  11. */  
  12. nt av_find_best_stream(AVFormatContext *ic,  
  13.                        enum AVMediaType type,  
  14.                        int wanted_stream_nb,  
  15.                        int related_stream,  
  16.                        AVCodec **decoder_ret,  
  17.                        int flags);  

avcodec_find_decoder

  1. /* 通过匹配的编解码器ID,找到一个注册的解码器. 
  2.  */  
  3. AVCodec *avcodec_find_decoder(enum AVCodecID id);  

avcodec_alloc_context3

  1. /* 分配一个AVCodecContext,并且设置它的字段为默认值。 
  2. * 这个结构体应该使用avcodec_free_context()释放 
  3. */  
  4. VCodecContext *avcodec_alloc_context3(const AVCodec *codec);  

avcodec_parameters_to_context

  1. /*根据AVCodecParameters的值填充AVCodecContext。 
  2.  *AVCodecParameters与AVCodecContext相对应的将会被替换,不对应不替换。 
  3.  */  
  4. int avcodec_parameters_to_context(AVCodecContext *codec,  
  5.                                   const AVCodecParameters *par);  

avcodec_open2

  1. /* 使用提供的AVCodec来初始化AVCodecContext结构体。在使用这个功能之前,AVCodecContext必须通过 
  2. * avcodec_alloc_context3()分配空间。 
  3. * 这些方法 avcodec_find_decoder_by_name(),avcodec_find_encoder_by_name()... 提供一个简单的方法来检索到一个编解码器。 
  4. *   这个方法不是线程安全的 
  5. */  
  6. nt avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);  

av_image_alloc

  1.  /* 通过参数w,h,pix_fmt,align来分配一个图像的空间,参数pointers和linesizes将会被赋值。 
  2.  * pointers必须通过av_freep(&pointers[0])释放。 
  3.  * @param 调整缓冲区大小对齐的值 
  4.  */  
  5. int av_image_alloc(uint8_t *pointers[4], int linesizes[4],  
  6.                    int w, int h, enum AVPixelFormat pix_fmt, int align);  

av_read_frame

  1. /* 返回流的下一帧到AVPacket中。 
  2.  * 这个方法返回存储在文件中的数据,它并不确定这些有效帧有解码器。 
  3.  * 每次调用这个方法,它将分割文件中的数据到AVPacket里。它不会遗漏 
  4.  * 有效帧之间的数据,来给解码器最大的信息来解码。 
  5.  * 
  6.  * 如果返回的AVPacket有效,在不使用时,必须用av_packet_unref释放。 
  7.  * 对于视频,AVPacket包含一帧。对于音频,如果每一帧大小固定已知,那么 
  8.  * AVPacket包含多帧;如果每一帧大小可变,那么AVPacket包含一帧。 
  9.  * 
  10.  * 这几个值(pkt->pts, pkt->dts and pkt->duration)总是会被赋值。 
  11.  */  
  12. int av_read_frame(AVFormatContext *s, AVPacket *pkt);  

struct AVPacket

  1.  /* 这个结构体存储压缩数据。它通过解析器输出,作为解码器的输入,或者通过编码器输出, 然后传给合成器。 
  2.  * 对于视频,它代表一个压缩帧,对于音频,它可能代表多个压缩帧,解码器允许输出一个空的packet, 
  3.  * 没有压缩数据,仅仅包含次要数据 
  4.  */  
  5. typedef struct AVPacket   

avcodec_send_packet

  1.  /* 将数据包发送给解码器。(可以想象解码内部有个存放数据的容器) 
  2.  */  
  3. int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);  

avcodec_receive_frame

  1. /* 返回解码过后的一个帧,与avcodec_send_packet配套使用。 
  2.  * avcodec_send_packet传入的AVPacket可能包含多帧,所以avcodec_receive_frame可以调用 
  3.  * 多次,直到失败。 
  4.  */  
  5. int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);  

源码

解析MP4文件得到视频帧和音频帧,并分别保存到不同的文件。(代码许多地方返回值未校验)
  1. extern "C"  
  2. {  
  3. #include <libavutil/imgutils.h>  
  4. #include <libavutil/samplefmt.h>  
  5. #include <libavutil/timestamp.h>  
  6. #include <libavformat/avformat.h>  
  7. }  
  8.   
  9. int get_format_from_sample_fmt(const char **fmt, enum AVSampleFormat sample_fmt)  
  10. {  
  11.     int i;  
  12.     struct sample_fmt_entry {  
  13.         enum AVSampleFormat sample_fmt;   
  14.         const char *fmt_be, *fmt_le;  
  15.     }   
  16.       
  17.     sample_fmt_entries[] = {  
  18.         { AV_SAMPLE_FMT_U8,  "u8",    "u8" },  
  19.         { AV_SAMPLE_FMT_S16, "s16be""s16le" },  
  20.         { AV_SAMPLE_FMT_S32, "s32be""s32le" },  
  21.         { AV_SAMPLE_FMT_FLT, "f32be""f32le" },  
  22.         { AV_SAMPLE_FMT_DBL, "f64be""f64le" },  
  23.     };  
  24.   
  25.     *fmt = NULL;  
  26.   
  27.     for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {  
  28.         struct sample_fmt_entry *entry = &sample_fmt_entries[i];  
  29.         if (sample_fmt == entry->sample_fmt) {  
  30.             *fmt = AV_NE(entry->fmt_be, entry->fmt_le);  
  31.             return 0;  
  32.         }  
  33.     }  
  34.   
  35.     fprintf(stderr,  
  36.         "sample format %s is not supported as output format\n",  
  37.         av_get_sample_fmt_name(sample_fmt));  
  38.     return -1;  
  39. }  
  40.   
  41. void demuxing_decoding(const char* filename)  
  42. {  
  43.     av_register_all();  
  44.     puts("注册...");  
  45.   
  46.     AVFormatContext* formatContext = nullptr;  
  47.     if (0 != avformat_open_input(&formatContext, filename, NULL, NULL))  
  48.     {  
  49.         puts("avformat_open_input失败.");  
  50.         return;  
  51.     }  
  52.     puts("avformat_open_input...");  
  53.       
  54.     if (avformat_find_stream_info(formatContext, NULL) < 0)  
  55.     {  
  56.         puts("avformat_find_stream_info失败.");  
  57.         return;  
  58.     }  
  59.     puts("avformat_find_stream_info...");  
  60.   
  61.     //视频  
  62.     int video_stream_idx = av_find_best_stream(formatContext, AVMEDIA_TYPE_VIDEO, 0, -1, nullptr, 0);  
  63.     if (video_stream_idx < 0)  
  64.     {  
  65.         puts("avformat_find_stream_info AVMEDIA_TYPE_VIDEO失败.");  
  66.         return;  
  67.     }  
  68.   
  69.     AVStream* video_st = formatContext->streams[video_stream_idx];  
  70.     AVCodec *videCodec = avcodec_find_decoder(video_st->codecpar->codec_id);  
  71.     AVCodecContext* videoContext = avcodec_alloc_context3(videCodec);  
  72.     avcodec_parameters_to_context(videoContext, video_st->codecpar);  
  73.     avcodec_open2(videoContext, videCodec, nullptr);  
  74.   
  75.     uint8_t *video_dst_data[4] = { NULL };  
  76.     int video_dst_linesize[4];  
  77.     int video_dst_bufsize = av_image_alloc(video_dst_data, video_dst_linesize, videoContext->width, videoContext->height, videoContext->pix_fmt, 1);  
  78.   
  79.     //音频  
  80.     int audio_stream_idx = av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);  
  81.     AVStream* audio_st = formatContext->streams[audio_stream_idx];  
  82.     AVCodec* audioCodec = avcodec_find_decoder(audio_st->codecpar->codec_id);  
  83.     AVCodecContext* audioContext = avcodec_alloc_context3(audioCodec);  
  84.     avcodec_parameters_to_context(audioContext, audio_st->codecpar);  
  85.     avcodec_open2(audioContext, audioCodec, nullptr);  
  86.   
  87.     av_dump_format(formatContext, 0, filename, 0);  
  88.   
  89.     AVFrame* frame = av_frame_alloc();  
  90.   
  91.     AVPacket pkt;  
  92.     av_init_packet(&pkt);  
  93.     pkt.data = NULL;  
  94.     pkt.size = 0;  
  95.   
  96.     FILE *videofile = nullptr;  
  97.     char video_name[128] = { 0 };  
  98.     sprintf_s(video_name, 128, "output\\demuxing_video.%s", videCodec->name);  
  99.     fopen_s(&videofile, video_name, "wb");  
  100.     FILE *audiofile = nullptr;  
  101.     char audio_name[128] = { 0 };  
  102.     sprintf_s(audio_name, 128, "output\\demuxing_audio.%s", audioCodec->name);  
  103.     fopen_s(&audiofile, audio_name, "wb");  
  104.   
  105.     while (av_read_frame(formatContext, &pkt) >= 0)   
  106.     {  
  107.         if (pkt.stream_index == video_stream_idx)  
  108.         {  
  109.             avcodec_send_packet(videoContext, &pkt);  
  110.             while (0 == avcodec_receive_frame(videoContext, frame))  
  111.             {  
  112.                 av_image_copy(video_dst_data, video_dst_linesize, (const uint8_t **)frame->data, frame->linesize, videoContext->pix_fmt, videoContext->width, videoContext->height);  
  113.                 fwrite(video_dst_data[0], 1, video_dst_bufsize, videofile);  
  114.                 puts("写入视频");  
  115.             }  
  116.         }  
  117.         else if (pkt.stream_index == audio_stream_idx)  
  118.         {  
  119.             avcodec_send_packet(audioContext, &pkt);  
  120.             while (0 == avcodec_receive_frame(audioContext, frame))  
  121.             {  
  122.                 size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)frame->format);  
  123.                 fwrite(frame->extended_data[0], 1, unpadded_linesize, audiofile);  
  124.                 puts("写入音频");  
  125.             }  
  126.         }  
  127.   
  128.         av_packet_unref(&pkt);  
  129.     }  
  130.   
  131.     printf("Play the output video file with the command:\n"  
  132.         "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",  
  133.         av_get_pix_fmt_name(videoContext->pix_fmt), videoContext->width, videoContext->height,  
  134.         video_name);  
  135.   
  136.     enum AVSampleFormat sfmt = audioContext->sample_fmt;  
  137.     int n_channels = audioContext->channels;  
  138.     const char *fmt;  
  139.   
  140.     if (av_sample_fmt_is_planar(sfmt)) {  
  141.         const char *packed = av_get_sample_fmt_name(sfmt);  
  142.         printf("Warning: the sample format the decoder produced is planar "  
  143.             "(%s). This example will output the first channel only.\n",  
  144.             packed ? packed : "?");  
  145.         sfmt = av_get_packed_sample_fmt(sfmt);  
  146.         n_channels = 1;  
  147.     }  
  148.   
  149.     if (get_format_from_sample_fmt(&fmt, sfmt) < 0)  
  150.     {  
  151.   
  152.     }  
  153.   
  154.     printf("Play the output audio file with the command:\n"  
  155.         "ffplay -f %s -ac %d -ar %d %s\n",  
  156.         fmt, n_channels, audioContext->sample_rate,  
  157.         audio_name);  
  158.   
  159.     fclose(videofile);  
  160.     fclose(audiofile);  
  161.     avcodec_free_context(&videoContext);  
  162.     avcodec_free_context(&audioContext);  
  163.     avformat_close_input(&formatContext);  
  164.     av_frame_free(&frame);  
  165.     av_free(video_dst_data[0]);  
  166.   
  167.     getchar();  
  168. }  



转自:http://blog.csdn.net/u013043408/article/details/52701776

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用ffmpeg复用TS流,可以使用以下命令: ``` ffmpeg -i input.ts -map 0 -c copy output.mp4 ``` 其中,`input.ts`是输入的TS流文件,`output.mp4`是输出的目标文件。这个命令会将输入的TS流复用并复制到目标文件中。 释一下命令的参数: - `-i input.ts`指定输入文件为`input.ts`,即要复用的TS流文件。 - `-map 0`表示将输入文件中的所有流都映射到输出文件中。 - `-c copy`表示使用原始的编码格式进行复制,不进行任何重新编码操作。这样可以保持原始的编码质量和格式。 - `output.mp4`是输出的目标文件名,可以根据需要自行指定。 这个命令会将TS流中的所有流(包括视频流、音频流等)复用,并将它们复制到输出文件中,不对流进行任何重新编码操作。这样可以快速地提取TS流中的音视频数据。 注意:这只是一个简单的示例命令,具体的使用方法和参数根据实际情况可能会有所不同。在实际使用中,可以根据需要调整命令参数来满足自己的需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [修改ffmpeg源码,并用它对多路节目TS流复用及播放](https://blog.csdn.net/cf125313/article/details/80764829)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [基于FFmpeg的视频播放器之二:复用](https://blog.csdn.net/caoshangpa/article/details/124486435)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [FLV复用代码实现!!!!](https://download.csdn.net/download/qq_39466755/87020098)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值