1/* 2 * SMJPEG muxer 3 * Copyright (c) 2012 Paul B Mahol 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/** 23 * @file 24 * This is a muxer for Loki SDL Motion JPEG files 25 */ 26 27#include "avformat.h" 28#include "internal.h" 29#include "mux.h" 30#include "smjpeg.h" 31 32typedef struct SMJPEGMuxContext { 33 uint32_t duration; 34} SMJPEGMuxContext; 35 36static int smjpeg_write_header(AVFormatContext *s) 37{ 38 AVDictionaryEntry *t = NULL; 39 AVIOContext *pb = s->pb; 40 int n, tag; 41 42 if (s->nb_streams > 2) { 43 av_log(s, AV_LOG_ERROR, "more than >2 streams are not supported\n"); 44 return AVERROR(EINVAL); 45 } 46 avio_write(pb, SMJPEG_MAGIC, 8); 47 avio_wb32(pb, 0); 48 avio_wb32(pb, 0); 49 50 ff_standardize_creation_time(s); 51 while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { 52 avio_wl32(pb, SMJPEG_TXT); 53 avio_wb32(pb, strlen(t->key) + strlen(t->value) + 3); 54 avio_write(pb, t->key, strlen(t->key)); 55 avio_write(pb, " = ", 3); 56 avio_write(pb, t->value, strlen(t->value)); 57 } 58 59 for (n = 0; n < s->nb_streams; n++) { 60 AVStream *st = s->streams[n]; 61 AVCodecParameters *par = st->codecpar; 62 if (par->codec_type == AVMEDIA_TYPE_AUDIO) { 63 tag = ff_codec_get_tag(ff_codec_smjpeg_audio_tags, par->codec_id); 64 if (!tag) { 65 av_log(s, AV_LOG_ERROR, "unsupported audio codec\n"); 66 return AVERROR(EINVAL); 67 } 68 avio_wl32(pb, SMJPEG_SND); 69 avio_wb32(pb, 8); 70 avio_wb16(pb, par->sample_rate); 71 avio_w8(pb, par->bits_per_coded_sample); 72 avio_w8(pb, par->ch_layout.nb_channels); 73 avio_wl32(pb, tag); 74 avpriv_set_pts_info(st, 32, 1, 1000); 75 } else if (par->codec_type == AVMEDIA_TYPE_VIDEO) { 76 tag = ff_codec_get_tag(ff_codec_smjpeg_video_tags, par->codec_id); 77 if (!tag) { 78 av_log(s, AV_LOG_ERROR, "unsupported video codec\n"); 79 return AVERROR(EINVAL); 80 } 81 avio_wl32(pb, SMJPEG_VID); 82 avio_wb32(pb, 12); 83 avio_wb32(pb, 0); 84 avio_wb16(pb, par->width); 85 avio_wb16(pb, par->height); 86 avio_wl32(pb, tag); 87 avpriv_set_pts_info(st, 32, 1, 1000); 88 } 89 } 90 91 avio_wl32(pb, SMJPEG_HEND); 92 93 return 0; 94} 95 96static int smjpeg_write_packet(AVFormatContext *s, AVPacket *pkt) 97{ 98 SMJPEGMuxContext *smc = s->priv_data; 99 AVIOContext *pb = s->pb; 100 AVStream *st = s->streams[pkt->stream_index]; 101 AVCodecParameters *par = st->codecpar; 102 103 if (par->codec_type == AVMEDIA_TYPE_AUDIO) 104 avio_wl32(pb, SMJPEG_SNDD); 105 else if (par->codec_type == AVMEDIA_TYPE_VIDEO) 106 avio_wl32(pb, SMJPEG_VIDD); 107 else 108 return 0; 109 110 avio_wb32(pb, pkt->pts); 111 avio_wb32(pb, pkt->size); 112 avio_write(pb, pkt->data, pkt->size); 113 114 smc->duration = FFMAX(smc->duration, pkt->pts + pkt->duration); 115 return 0; 116} 117 118static int smjpeg_write_trailer(AVFormatContext *s) 119{ 120 SMJPEGMuxContext *smc = s->priv_data; 121 AVIOContext *pb = s->pb; 122 int64_t currentpos; 123 124 if (pb->seekable & AVIO_SEEKABLE_NORMAL) { 125 currentpos = avio_tell(pb); 126 avio_seek(pb, 12, SEEK_SET); 127 avio_wb32(pb, smc->duration); 128 avio_seek(pb, currentpos, SEEK_SET); 129 } 130 131 avio_wl32(pb, SMJPEG_DONE); 132 133 return 0; 134} 135 136const AVOutputFormat ff_smjpeg_muxer = { 137 .name = "smjpeg", 138 .long_name = NULL_IF_CONFIG_SMALL("Loki SDL MJPEG"), 139 .priv_data_size = sizeof(SMJPEGMuxContext), 140 .audio_codec = AV_CODEC_ID_PCM_S16LE, 141 .video_codec = AV_CODEC_ID_MJPEG, 142 .write_header = smjpeg_write_header, 143 .write_packet = smjpeg_write_packet, 144 .write_trailer = smjpeg_write_trailer, 145 .flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT, 146 .codec_tag = (const AVCodecTag *const []){ ff_codec_smjpeg_video_tags, ff_codec_smjpeg_audio_tags, 0 }, 147}; 148