Advertisement
Guest User

avformat remux rtsp to mp4

a guest
Feb 14th, 2020
161
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 11.33 KB | None | 0 0
  1. /*
  2.  ============================================================================
  3.  Name        : ffmpegTest.c
  4.  Author      :
  5.  Version     :
  6.  Copyright   : Your copyright notice
  7.  Description : Hello World in C, Ansi-style
  8.  ============================================================================
  9.  */
  10.  
  11. #include <libavcodec/avcodec.h>
  12. #include <libavformat/avformat.h>
  13. #include <libavdevice/avdevice.h>
  14. #include <libavutil/timestamp.h>
  15. #include <inttypes.h>
  16. #include "stdbool.h"
  17.  
  18.  
  19. #define URL "CAMERARTSPLINK"
  20.  
  21. int BufferSize = 512;
  22. int MaxTransferErrors = 100;
  23. AVFormatContext* mInputFormatCtx;
  24. AVFormatContext* mOutputFormatCtx;
  25. int mInputVideoStreamId;
  26. int64_t mOutputLastDTS;
  27.  
  28. char* mInputFormat = "rtsp";
  29. char* mInputStreamUrl = URL;
  30. uint8_t* mOutputBuffer;
  31.  
  32. int openInput();
  33. void closeInput();
  34. int openOutput();
  35. int writeOutput(void *opaque, uint8_t *buffer, int buffer_size);
  36. void closeOutput();
  37.  
  38. int main() {
  39.   mInputFormatCtx = mOutputFormatCtx = 0;
  40.  
  41.   av_register_all();
  42.  
  43.   // Make all formats available.
  44.   avdevice_register_all();
  45.  
  46.   // Initialize network functions
  47.   avformat_network_init();
  48.  
  49.   // Opening input stream
  50.   if (0 > openInput()) {
  51.     return 0;
  52.   }
  53.  
  54.   if (0 > openOutput()) {
  55.     return 0;
  56.   }
  57.  
  58.   long long i = 0;
  59.   AVPacket pkt;
  60.   int result;
  61.   bool requestShutdown = false;
  62.   bool fix_dts;
  63.  
  64.   while (!requestShutdown) {
  65.     memset(&pkt, 0, sizeof(AVPacket));
  66.  
  67.     // Read encoded frame (as a packet).
  68.     if (av_read_frame(mInputFormatCtx, &pkt) != 0) {
  69.       fprintf(stderr, "unable to read frame");
  70.       closeInput();
  71.       closeOutput();
  72.       av_packet_unref(&pkt);
  73.       break;
  74.     }
  75.  
  76.     // Ignore it if it's not our video stream.
  77.     if (pkt.stream_index != mInputVideoStreamId) {
  78.       av_packet_unref(&pkt);
  79.       continue;
  80.     }
  81.  
  82.     AVStream* const inStream = mInputFormatCtx->streams[pkt.stream_index];
  83.     if (!inStream) {
  84.       fprintf(stderr, "input stream not found with stream index %d", pkt.stream_index);
  85.       closeInput();
  86.       closeOutput();
  87.       av_packet_unref(&pkt);
  88.       break;
  89.     }
  90.  
  91.     // If there are multiple input streams, then the stream index on the packet
  92.     // may not match the stream index in our output. We need to ensure the index
  93.     // matches. Note by this point we have checked that it is indeed a packet
  94.     // from the stream we want (we do this when reading the packet).
  95.     //
  96.     // As we only ever have a single output stream (one, video), the index will
  97.     // be 0.
  98.     if (0 != pkt.stream_index) {
  99.       pkt.stream_index = 0;
  100.     }
  101.  
  102.     AVStream* const outStream = mOutputFormatCtx->streams[pkt.stream_index];
  103.     if (!outStream) {
  104.       fprintf(stderr, "output stream not found with stream index %d", pkt.stream_index);
  105.       closeInput();
  106.       closeOutput();
  107.       av_packet_unref(&pkt);
  108.       break;
  109.     }
  110.  
  111.     // It is possible that the input is not well formed. Its dts (decompression
  112.     // timestamp) may fluctuate. av_write_frame() says that the dts must be
  113.     // strictly increasing.
  114.     //
  115.     // Packets from such inputs might look like:
  116.     //
  117.     // in: pts:18750 pts_time:0.208333 dts:18750 dts_time:0.208333 duration:3750 duration_time:0.0416667 stream_index:1
  118.     // in: pts:0 pts_time:0 dts:0 dts_time:0 duration:3750 duration_time:0.0416667 stream_index:1
  119.     //
  120.     // dts here is 18750 and then 0.
  121.     //
  122.     // If we try to write the second packet as is, we'll see this error:
  123.     // [mp4 @ 0x10f1ae0] Application provided invalid, non monotonically increasing dts to muxer in stream 1: 18750 >= 0
  124.     //
  125.     // This is apparently a fairly common problem. In ffmpeg.c (as of ffmpeg
  126.     // 3.2.4 at least) there is logic to rewrite the dts and warn if it happens.
  127.     // Let's do the same. Note my logic is a little different here.
  128.     fix_dts = pkt.dts != AV_NOPTS_VALUE &&
  129.         mOutputLastDTS != AV_NOPTS_VALUE &&
  130.         pkt.dts <= mOutputLastDTS;
  131.  
  132.     // It is also possible for input streams to include a packet with
  133.     // dts/pts=NOPTS after packets with dts/pts set. These won't be caught by the
  134.     // prior case. If we try to send these to the encoder however, we'll generate
  135.     // the same error (non monotonically increasing DTS) since the output packet
  136.     // will have dts/pts=0.
  137.     fix_dts |= pkt.dts == AV_NOPTS_VALUE && mOutputLastDTS != AV_NOPTS_VALUE;
  138.  
  139.     if (fix_dts) {
  140.         int64_t const next_dts = mOutputLastDTS + 1;
  141.  
  142.         fprintf(stderr, "Warning: Non-monotonous DTS in input stream. \n"
  143.                 "Previous: %ld\n"
  144.                 "current: %ld\n"
  145.                 "Changing to %ld.\n", mOutputLastDTS, pkt.dts, next_dts);
  146.  
  147.         // We also apparently (ffmpeg.c does this too) need to update the pts.
  148.         // Otherwise we see an error like:
  149.         //
  150.         // [mp4 @ 0x555e6825ea60] pts (3780) < dts (22531) in stream 0
  151.  
  152.         if (pkt.pts != AV_NOPTS_VALUE && pkt.pts >= pkt.dts) {
  153.             pkt.pts = FFMAX(pkt.pts, next_dts);
  154.         }
  155.         // In the case where pkt->dts was AV_NOPTS_VALUE, pkt->pts can be
  156.         // AV_NOPTS_VALUE too which we fix as well.
  157.         if (pkt.pts == AV_NOPTS_VALUE) {
  158.             pkt.pts = next_dts;
  159.         }
  160.  
  161.         pkt.dts = next_dts;
  162.     }
  163.  
  164.  
  165.     // Set pts/dts if not set. Otherwise we will receive warnings like
  166.     //
  167.     // [mp4 @ 0x55688397bc40] Timestamps are unset in a packet for stream 0. This
  168.     // is deprecated and will stop working in the future. Fix your code to set
  169.     // the timestamps properly
  170.     //
  171.     // [mp4 @ 0x55688397bc40] Encoder did not produce proper pts, making some up.
  172.     if (pkt.pts == AV_NOPTS_VALUE) {
  173.         pkt.pts = 0;
  174.     } else {
  175.         pkt.pts = av_rescale_q_rnd(pkt.pts, inStream->time_base,
  176.                 outStream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
  177.     }
  178.  
  179.     if (pkt.dts == AV_NOPTS_VALUE) {
  180.         pkt.dts = 0;
  181.     } else {
  182.         pkt.dts = av_rescale_q_rnd(pkt.dts, inStream->time_base,
  183.                 outStream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
  184.     }
  185.  
  186.     pkt.duration = av_rescale_q(pkt.duration, inStream->time_base,
  187.             outStream->time_base);
  188.     pkt.pos = -1;
  189.  
  190.     // Track last dts we see (see where we use it for why).
  191.     mOutputLastDTS = pkt.dts;
  192.  
  193.  
  194.     // Write encoded frame (as a packet).
  195.  
  196.     // av_interleaved_write_frame() works too, but I don't think it is needed.
  197.     // Using av_write_frame() skips buffering.
  198.     result = av_write_frame(mOutputFormatCtx, &pkt);
  199.     if (0 != result) {
  200.         fprintf(stderr, "unable to write frame.");
  201.         fprintf(stderr, "%s", av_err2str(result));
  202.         closeInput();
  203.         closeOutput();
  204.         av_packet_unref(&pkt);
  205.         break;
  206.     }
  207.  
  208.     av_packet_unref(&pkt);
  209.  
  210.     i++;
  211.     if (i == 1000) {
  212.         requestShutdown = true;
  213.     }
  214.   }
  215.  
  216.   closeInput();
  217.   closeOutput();
  218. }
  219.  
  220. int openInput() {
  221.     AVInputFormat* const inputFormat = av_find_input_format(mInputFormat);
  222.     if (!inputFormat) {
  223.         fprintf(stderr, "input format not found");
  224.         return -2;
  225.     }
  226.  
  227.     AVDictionary* opts = NULL;
  228.  
  229.     int const openStatus = avformat_open_input(&mInputFormatCtx, mInputStreamUrl, inputFormat, &opts);
  230.     if (0 != openStatus) {
  231.         fprintf(stderr, "unable to open input.");
  232.         fprintf(stderr, "%s", av_err2str(openStatus));
  233.         closeInput();
  234.         return -4;
  235.     }
  236.  
  237.     if (0 > avformat_find_stream_info(mInputFormatCtx, &opts)) {
  238.         fprintf(stderr, "failed to find stream info");
  239.         closeInput();
  240.         return -5;
  241.     }
  242.  
  243.     av_dict_free(&opts);
  244.  
  245.     // Find the first video stream.
  246.     mInputVideoStreamId = -1;
  247.     for (unsigned int i = 0; i < mInputFormatCtx->nb_streams; i++) {
  248.         AVStream* const inStream = mInputFormatCtx->streams[i];
  249.  
  250.         if (inStream->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
  251.             continue;
  252.         }
  253.  
  254.         mInputVideoStreamId = (int)i;
  255.         break;
  256.     }
  257.  
  258.     if (-1 == mInputVideoStreamId) {
  259.         fprintf(stderr, "no video stream found");
  260.         closeInput();
  261.         return -6;
  262.     }
  263.  
  264.     return 0;
  265. }
  266.  
  267. void closeInput() {
  268.     if (mInputFormatCtx) {
  269.         avformat_close_input(&mInputFormatCtx);
  270.     }
  271. }
  272.  
  273. int openOutput() {
  274.  
  275.     AVOutputFormat* const outputFormat = av_guess_format("mp4", NULL, NULL);
  276.     if (!outputFormat) {
  277.         fprintf(stderr, "output format not found");
  278.         closeOutput();
  279.         return -1;
  280.     }
  281.  
  282.     if (0 != mOutputBuffer) {
  283.         fprintf(stderr, "output buffer is not empty!");
  284.         closeOutput();
  285.         return -2;
  286.     }
  287.  
  288.     mOutputBuffer = (uint8_t *)av_malloc(BufferSize);
  289.  
  290.     int const allocStatus = avformat_alloc_output_context2(&mOutputFormatCtx, outputFormat, NULL, NULL);
  291.     if (0 != allocStatus) {
  292.         fprintf(stderr, "unable to allocate output context.");
  293.         fprintf(stderr, "%s", av_err2str(allocStatus));
  294.         closeOutput();
  295.         return -3;
  296.     }
  297.  
  298.     mOutputFormatCtx->pb = avio_alloc_context(
  299.                                     mOutputBuffer, BufferSize,  // internal buffer and its size
  300.                                     1,                          // write flag (1=true, 0=false)
  301.                                     0,                          // user data, will be passed to our cb
  302.                                     0,                          // no read
  303.                                     &writeOutput,               // write callback function
  304.                                     0                           // no seek
  305.                             );
  306.     mOutputFormatCtx->flags |= AVFMT_FLAG_CUSTOM_IO;
  307.     mOutputFormatCtx->oformat = outputFormat;
  308.  
  309.     // Copy the input video stream.
  310.     AVStream* const outStream = avformat_new_stream(mOutputFormatCtx, NULL);
  311.     if (!outStream) {
  312.         fprintf(stderr, "unable to add stream");
  313.         closeOutput();
  314.         return -4;
  315.     }
  316.  
  317.     AVStream* const inStream = mInputFormatCtx->streams[mInputVideoStreamId];
  318.  
  319.     if (0 > avcodec_parameters_copy(outStream->codecpar, inStream->codecpar)) {
  320.         fprintf(stderr, "unable to copy codec parameters");
  321.         closeOutput();
  322.         return -5;
  323.     }
  324.  
  325.     // Write file header.
  326.     AVDictionary* opts = NULL;
  327.  
  328.     // -movflags frag_keyframe tells the mp4 muxer to fragment at each video
  329.     // keyframe. This is necessary for it to support output to a non-seekable
  330.     // file (e.g., pipe).
  331.     //
  332.     // -movflags isml+frag_keyframe is the same, except isml appears to be to
  333.     // make the output a live smooth streaming feed (as opposed to not live). I'm
  334.     // not sure the difference, but isml appears to be a microsoft
  335.     // format/protocol.
  336.     //
  337.     // To specify both, use isml+frag_keyframe as the value.
  338.     //
  339.     // I found that while Chrome had no trouble displaying the resulting mp4 with
  340.     // just frag_keyframe, Firefox would not until I also added empty_moov.
  341.     // empty_moov apparently writes some info at the start of the file.
  342.     if (0 > av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0)) {
  343.         fprintf(stderr, "unable to set movflags opt");
  344.         closeOutput();
  345.         return -6;
  346.     }
  347.  
  348.     if (av_dict_set_int(&opts, "flush_packets", 1, 0) < 0) {
  349.         fprintf(stderr, "unable to set flush_packets opt");
  350.         closeOutput();
  351.         av_dict_free(&opts);
  352.         return -7;
  353.     }
  354.  
  355.     if (0 > avformat_write_header(mOutputFormatCtx, &opts)) {
  356.         fprintf(stderr, "unable to write header");
  357.         closeOutput();
  358.         av_dict_free(&opts);
  359.         return -8;
  360.     }
  361.  
  362.     // Check any options that were not set. Because I'm not sure if all are
  363.     // appropriate to set through the avformat_write_header().
  364.     if (0 != av_dict_count(opts)) {
  365.         fprintf(stderr, "some options not set");
  366.         closeOutput();
  367.         av_dict_free(&opts);
  368.         return -9;
  369.     }
  370.  
  371.     av_dict_free(&opts);
  372.  
  373.     mOutputLastDTS = AV_NOPTS_VALUE;
  374.  
  375.     return 0;
  376. }
  377.  
  378. int writeOutput(void *opaque, uint8_t *buffer, int buffer_size) {
  379.     printf("writeOutput: writing %d bytes: ", buffer_size);
  380.     for(int i=0;i<buffer_size;i++) {
  381.         printf("%d, ", (int)(*(buffer+i)));
  382.     }
  383.     printf("\n");
  384.  
  385.     // Always return the buffer size.
  386.     return buffer_size;
  387. }
  388.  
  389. void closeOutput() {
  390.     if (mOutputFormatCtx) {
  391.         if (0 != av_write_trailer(mOutputFormatCtx)) {
  392.             fprintf(stderr, "unable to write trailer");
  393.         }
  394.  
  395.         av_freep(&mOutputBuffer);
  396.         av_freep(&mOutputFormatCtx->pb);
  397.  
  398.  
  399.         avformat_free_context(mOutputFormatCtx);
  400.     }
  401. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement